JavaScrip 继承2.0 改变构造函数的原型并不是继承 假设有两个构造函数 User 和 Person . 现在想让 Person 的实例能够访问 User 的 showName() 方法。此时一种思路:直接将 Person 的原型修改为 User 的原型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function User (name ) { this .name = name } User.prototype.showName = function ( ) { console .log(this .name) }function Person (name ) { this .name = name } Person.prototype = User.prototypelet person = new Person("xiaofang" ) person.showName() console .log(person.constructor === User) console .log(person.constructor === Person)
本质上,将 User 直接作为 person 的构造函数,这样 person 就能够访问 User 原型对象的方法。
但是,这导致了一个问题:假设此时要给所有 person 实例添加一个 age 属性,由于 Person.prototype 指向了 User.prototype ,二者也就是同一个对象。因此给 Person.prototype 添加属性会导致 User 的原型也被添加了这个属性:
Person.prototype.age = 18 console .log(Person.prototype) console .log(User.prototype)
这显然不是我们想要的。
真正的继承应该是可以用到父辈的方法,同时自己添加或修改自己方法/属性的时候不会反过来影响父辈。
继承是原型的继承 首先为了便于理解,使用 __proto__ 的写法来寻找和设置实例对象的原型:
let f = {}console .log(Object .getPrototypeOf(f)) 等价于:console .log(__proto__)Object .setPrototype(f,obj) 等价于: f.__proto__ = objlet f2 = Object .create(f) f2.__proto__ = f
以上代码 setPrototypeOf() 和 Object.create() 二者几乎没有差别,只是前者设置“父亲”,后者设置“儿子”
由于构造函数和实例之间有这样一对关系:
Obj.prototype === obj.__proto__
即:构造函数的“叔叔”是实例对象的“爸爸”。
正常从原型链访问方法,都是延着父辈这条链访问,只要方法存在于父辈这条链上,就能够访问得到。并且,自己可以修改自己的方法/属性,并不会影响到父辈。
因此,如果想在继承其他构造函数时,实例对象自己能够添加和修改自身方法/属性,而不影响父辈。就不能去修改构造函数的原型,即不能改变原有“爸爸”。
正确的做法是,让待继承构造函数的“叔叔”进入实例对象的父亲链,而不是直接作为父亲。
因此,可以让实例对象的原型继承构造函数的prototype。即:Person.prototype.__proto__ = User.prototype。
也就是说,不能让构造函数的“叔叔”作为实例对象的“爸爸”,但是可以让他作为实例对象的“爷爷”:
Person.prototype.__proto__ = User.prototype Person.prototype = new User() Person.prototype = Object .create(User.prototype)Object .setPrototype(Person.prototype, User.prototype) Person.prototype.age = 18 let person = new Person("xiaofang" )console .log(Person.prototype) console .log(User.prototype)
继承的解决方案 一般使用所谓的组合模式 来实现继承:
使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function User (name ) { this .name = name } User.prototype.showName = function ( ) {}function Person (name,age ) { User.call(this ,name) this .age = age }function Temp ( ) {} Temp.prototype = User.prototype Person.prototype = new Temp()Object .defineProperty(Person.prototype,"constructor" ,{ value : Person, enumerable : false }) Person.prototype.sayAge = function ( ) {}let person1 = new Person("xiaofang" ,18 )
类构造函数实现继承 class User { constructor (name ) { this .name = name } showName ( ) {} }class Person extends User { constructor (age ) { super () this .age = age } sayAge ( ) {} }let person1 = new Person("xiaofang" ,18 )
instanceof 的实现原理 instanceof 用于判断一个构造函数的原型是否在一个实例对象的原型链上。
例如, a instanceof B 表示判断 B 的 叔叔是否是 a 的长辈
console .log(person1 instanceof User)
这个关键字的实现原理就是,沿着实例对象的原型链向上攀升,看找不找得到构造函数的“叔叔”。以下使用一个函数来说明 instanceof 关键字的实现原理:
function checkPrototype (obj,constructor ) { if (!obj.__proto__) return false if (obj.__proto__ === constructor .prototype ) return true return checkPrototype (obj.__proto__,constructor ) } //测试:checkPrototype (person1, User ) // true
instenceof 和 isPrototypeOf() 的区别
console .log(person1 instanceof User) console .log(User.isPrototypeOf(person1)) console .log(User.prototype.isPrototypeOf(person1)) console .log(User.isPrototypeOf(Person))