对象属性 | __proto__ |
prototype |
---|---|---|
普通对象 | √ | × |
函数对象 | √ | √ |
function Person(name,age){ this.name = name; this.age = age; Person.prototype.say = function(){ console.log(`my name is ${this.name} and ${this.age} years`) } } let p1 = new Person('pfzz',25)
p1是普通对象
Person则是函数对象java
// prototype是函数对象的属性,其本质是 一个对象,包含了__proto__与constructor, // 只看普通对象p1,p1只有__proto__,而且指向建立它的函数对象的prototype console.info(p1) console.info(p1.__proto__ === Person.prototype) console.info(p1.__proto__.constructor === Person) console.info(p1.__proto__.__proto__ === Object.prototype) console.info(p1.__proto__.__proto__.constructor === Object) console.info(p1.__proto__.__proto__.__proto__ === null) // 函数对象Person,有__proto__、prototype属性 console.info(Person.__proto__ === Function.prototype) console.info(Person.__proto__.constructor === Function) console.info(Person.__proto__.__proto__ === Object.prototype) console.info(Person.__proto__.__proto__.constructor === Object) console.info(Person.__proto__.__proto__.__proto__ === null) console.info(Person.prototype.constructor === Person) console.info(Person.prototype.__proto__ === Object.prototype) console.info(Person.prototype.__proto__.constructor === Object) console.info(Person.prototype.__proto__.__proto__ === null) // 综上具备在指向的只有__proto__与constructor属性, // 而constructor属性老是指向prototype所在的函数对象,造成闭环。 // __proto__最终老是指向null
由上可知javasript的继承就要靠__proto__来实现es6
// 一、类式继承 function Child1(name,age,sex){ this.sex = sex; const prototype = new Person(name,age); prototype.play = function(){ console.log(`my name is ${this.name} and ${this.age} years and ${this.sex}`) }; this.__proto__ = prototype } const kind1 = new Child1('pfzzz',25,'man') kind1.say() console.info(kind1) console.info(kind1.__proto__ !== Child1.prototype) 缺点:继承属性被放在原型链上,致使kind1.__proto__ !== Child1.prototype
// 二、构造函数式继承 function Child2(name,age,sex){ this.sex = sex; Person.call(this,name,age) } const kind2 = new Child2('pfzzz',25,'man') console.info(kind2) kind2.say() // 报错 缺点:原型链上的方法没有被继承
// 三、组合式继承 function Child3(name,age,sex){ this.sex = sex; Person.call(this,name,age) // 第一次 }; Child3.prototype = new Person() // 第二次 const kind3 = new Child3('pfzzz',25,'man') console.info(kind3) kind3.say() 缺点:子类没法传递动态参数给父类,且父类构造函数被调用两次,问题不大。
// 四、野路子继承 function inherit(child, parent) { // 将父类原型和子类原型合并,并赋值给子类的原型 child.prototype = Object.assign(Object.create(parent.prototype), child.prototype) // 重写被污染的子类的constructor p.constructor = child } // 缺点:Object.assign并非最好的方法
//五、利用es5的Reflect来优化 function fancyShadowMerge(target, source) { for (const key of Reflect.ownKeys(source)) { Reflect.defineProperty(target, key, Reflect.getOwnPropertyDescriptor(source, key)) } return target } // Core function inherit(child, parent) { const objectPrototype = Object.prototype // 继承父类的原型 const parentPrototype = Object.create(parent.prototype) let childPrototype = child.prototype // 若子类没有继承任何类,直接合并子类原型和父类原型上的全部方法 // 包含可枚举/不可枚举的方法 if (Reflect.getPrototypeOf(childPrototype) === objectPrototype) { child.prototype = fancyShadowMerge(parentPrototype, childPrototype) } else { // 若子类已经继承子某个类 // 父类的原型将在子类原型链的尽头补全 while (Reflect.getPrototypeOf(childPrototype) !== objectPrototype) { childPrototype = Reflect.getPrototypeOf(childPrototype) } Reflect.setPrototypeOf(childPrototype, parent.prototype) } // 重写被污染的子类的constructor parentPrototype.constructor = child }
其余还有寄生式继承,寄生组合式继承,但都有一些反作用。
这些都是es5的继承,如今有了es6的 class
,相关继承就不须要在过多考虑了,这些属于拓展知识,知道最好不知道也不影响代码书写。函数