1.什么是对象原型?
2.获取对象原型的方法?
3.对象原型的作用?
原型:JavaScript当中每个对象都有一个特殊的内置属性[[prototype]],这个特殊的对象可以指向另一个对象
1.不能这样获取obj.[[prototype]]
2.这样获取obj.__proto__
使用的时候需要判断一下。
这个是浏览器实现的。对象原型,也可以称为隐式原型。因为一般不常用。
下文为了方便使用\_\_proto\_\_
当我们通过[[get]]的方式获取一个属性对应的value时,
它会优先在自己的对象中查找,如果找到直接返回。
如果没有找到,那么会在原型对象中找。
1.什么是函数的显式原型,和对象原型的区别?2.函数原型的作用?3.案例Student,将所有的函数定义放到了显式原型上。4.将函数看成是一个普通对象的时候,具备[[prototype]]属性。5.将函数看成是一个函数时,具备prototype属性。对象没有prototype,可以称为显式原型,一般直接用的。
是因为函数才有prototype,不是因为是对象。
对象的原型作用:
函数原型的作用:
1.创建一个空对象2.将这个空对象赋值给this3.将函数的显式原型赋值给这个对象作为它的隐式原型。4.执行函数体中的代码5.将这个对象默认返回。function Foo() { // 1. 创建空的对象 // 2. 将Foo的prototype(显式)赋值给空的对象的__proto__(隐式) } console.log(Foo.prototype)var f1 = new Foo() var f2 = new Foo()console.log(f1.proto) console.log(f1.proto === Foo.prototype) // true console.log(f1.proto === f2.proto) // true 构造出来的对象的隐式原型都是同一个对象Foo.prototype。new第三步意味着什么?
意味着我们通过Person构造函数创建出来的所有对象的\[\[prototype\]\]属性都指向Person.prototype
疑问:这个同一个对象有什么作用?
当我们多个对象用于共同的值时,我们可以将它放到构造函数对象的显示原型上,
由构造函数创建出来的所有对象,都会共享这些属性。
function Studnet(name, age) {this.name = namethis.age = agethis.studying = function() {console.log(this.name + " is studying")}
}
var stu1 = new Student("Kobe", 18)
var stu2 = new Studnet("Curry", 18)
console.log(stu1.studying === stu2.studying)
// false,重新创建了函数,我们不需要不同的函数。
// 因为隐式原型就是Student.prototype
Student.prototype.studying = function() {console.log(this.name + " is studying")
}
stu1.studying()
// 通过stu1隐式原型找到studying方法。this指向stu1(隐式绑定)
1.constructor2.操作属性3.结合内存图理解操作function Person(name, age) { this.name = name this.age = age }Person.prototype.running = function() { console.log(“running~”) }console.log(Person.prototype.constructor === Person) // truevar p1 = new Person(“Kobe”, 18) var p2 = new Person(“Curry”, 30)// 进行操作 console.log(p1.name) console.log(p2.name)p1.running() p2.running()// 新增属性 Person.prototype.address = “中国” p2.isAdmin = true // 添加p2的属性,原型对象上无isAdmin// 获取属性 console.log(p1.address)console.log(p1.isAdmin) // undefined console.log(p2.isAdmin)// 修改address p1.address = “广州” console.log(p2.address) // 中国总结:
1.构造函数上面有prototype属性,它指向构造函数的显式原型对象,显式原型对象中有constructor属性,指向了构造函数。
2.所有由new关键字创建的对象的[[prototype]]属性,指向构造函数的显式原型对象,在显式原型对象中每个添加的方法属性,指向对应的方法对象。
使用场景:如果我们需要在原型上添加过多的属性,通常重写原型对象。
Person.prototype.message = "Hello World"
Person.prototype.info = {name: "wang", age: 18}
Person.prototype.running = function() {console.log("running~")}
/// 让构造函数的显式原型不再指向原有的默认指向的原型对象,指向一个新的空对象
Person.prototype = {message: "Hello World",info: {name: "wang", age: 18},running: function() {console.log("running~")},constructor: Person
}
console.log(Object.keys(Person.prototype))
// 这个constructor是可以枚举的。直接添加不可枚举,如果需要一致
Object.defineProperty(Person.prototype, "constructor", {configurable: true,enumerable: false,value: Person,writable: true
})
如果不写constructor: Person,这个constructor属性,会指向Object构造函数。
constructor默认属性描述符:
1.概念
2.继承的思想
JavaScript支持面向对象编程也支持函数式编程。
面向对象编程 -->编程方式(范式)
怎么去创建一个类,根据这个类创建对象,然后操作。
1.定义
2.默认原型链,自定义原型链
3.查找顺序,结合图
4.补充
定义:
1.默认原型链2.自定义的原型链var obj = { name: “wang”, age: 18 } console.log(obj.message) obj.proto = { // message: “hello a” } obj.proto.proto = { // message: “hello b” } obj.proto.proto.proto = { message: “hello c” } console.log(obj.message) console.log(obj.proto.proto.proto.proto === Object.prototype) console.log(obj.proto.proto.proto.proto.proto === null)查找顺序
1.obj上面查找
2.obj.__proto__上面查找
3.obj.__proto__.__proto__==>null 上面查找 undefined
补充:
原型链的顶层:null
所有构造函数继承Object构造函数。即Object是所有类的一个父类
function Student(name, age, score) {this.score = score
}
Student.prototype.fighting = function() {}
function Person(name, age) {this.name = name this.age = age
}
Person.prototype.running = function() {console.log("running~")
}
var stu1 = new Student()
// 需要stu1调用running,自身的原型上去找,没有,再去原型的原型上找,指向的是Person.prototype
// 如何让二者关联?
方式一:父类的原型直接赋值给子类的原型 (错误的做法)
缺点:父类和子类共享一个原型对象,修改一个,另外一个也被修改。
Student.prototype = Person.prototype
Student.prototype.studying = function() {console.log("studying~")
}
var p = new Person()
p.studying()// 可以调用
方式二:创建一个父类的实例对象,用这个实例作为子类的原型对象。
var p1 = new Person("wang", 18)
Student.prototype = p1
解决了方式一缺点,
缺点:
继承了实例对象的属性。
如果删除子类中的赋值属性的代码,也会打印出属性,
但是打印的是继承父类的属性。
原型链继承的弊端:
1.某些属性其实是保存在p1对象上的;
2.直接打印对象看不到这个属性。
3.这个属性会被多个对象共享,如果这个对象是一个引用类型,那么就会造成问题。
4.不能给Person传递参数(让每个stu有自己的属性),因为这个对象是一次性创建的(没办法定制化)。
做法:在子类的构造函数内部调用父类的构造函数
function Student(name, age, score) {Person.call(this, name, age) // 借用构造函数,调用时是Student对象this.score = score
}
Student.prototype.fighting = function() {}
function Person(name, age) {this.name = name this.age = age
}
Person.prototype.running = function() {console.log("running~")
}
var p1 = new Person("wang", 18)
Student.prototype = p1
var stu1 = new Student("Kobe", 18, 100)
console.log(stu1.age)
console.log(stu1.name)
console.log(stu1)
加上原型链继承就是组合借用继承。
缺点:
组合继承最大的问题就父类的构造函数至少会被调用两次。
事实上,所有的子类实例事实上会拥有两份的父类属性。一个是本身,一个是原型上。
1.必须创建出来一个对象2.将这个对象的隐式原型属性指向父类的显式原型属性3.将这个对象赋值给子类的显式原型function Person() {} function Student() {} // 之前的做法 // var p = new Person() // Studnet.prototype = p // 方案一 // var obj = {} // Object.setPrototypeOf(obj, Person.prototype) // Student.prototuype = obj// 方案二 // function F() {} // F.prototype = Person.prototype // Student.Prototype = new F()// 方案三 // var obj = Object.create(Person.prototype) // console.log(obj.proto === Person.prototype) // Student.prototype = obj// 封装工具函数 function inherit(Subtype, Supertype) { Subtype.prototype = Object.create(Supertype.prototype) Object.defineProperty(Subtype.prototype, “constructor”, { enumerable: false, configurable: true, wriable: true, value: Subtype }) }inherit(Student, Person)// 考虑兼容性问题 Object.create function createObject(o) { function F() {} F.prototype = o return new F() }将Subtype和Supertype联系在一起,称为寄生式函数。
整个过程称为寄生组合式继承。
寄生组合式继承:
1.原型链2.借用构造函数3.原型式(对象之间)4.寄生式函数function inherit(Subtype, Supertype) { Subtype.prototype = Object.create(Supertype.prototype) Object.defineProperty(Subtype.prototype, “constructor”, { enumerable: false, configurable: true, wriable: true, value: Subtype }) } function Person(name, age) { this.name = name this.age = age } Person.prototype.running = function() { console.log(“running~”) }function Student(name, age, score) { Person.call(this, name, age) this.score = score }inherit(Student, Person) Student.prototype.studying = function() { console.log(“studying~”) }var stu1 = new Student(“Kobe”, 30, 10000000) console.log(stu1) stu1.studying() stu1.running()
完美解决
Douglas Crockford研究对象之间的继承。实际上实现继承并不好。
var obj = {name: "wang",age: 18
}
// 原型式继承
var info = {}
info.__proto__ = obj
// 如果创建多个继承的对象,出现大量重复代码
// 使用原型式继承__proto__会出现浏览器兼容性问题,因此没有用
function createObject(o) {function F() {}F.prototype = oreturn new F()
}
// var info1 = createObject(obj)
// info1.height = 188
// var info2 = createObject(obj)
// info2.height = 199
// 目前的代码有弊端,没有自己特有的属性,需要单独添加。
// 寄生式函数
function createInfo(o, name, age, height) {var newObj = createObject(o)newObj.name = namenewObj.age = agenewObj.height = heightreturn newObj
}
// 创建一系列对象
var info1 = createInfo(obj, "Kobe", 18, 222)
console.log(info1)
内存图:
整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。
有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享
部分文档展示:
文章篇幅有限,后面的内容就不一一展示了
有需要的小伙伴,可以点下方卡片免费领取