JavaScript原型继承重写prototype后已有实例的原型问题
问题复现代码
//debugger; function User(email,name) { this.email = email; this.name = name; this.online = false; } User.prototype.login = function() { this.online = true; console.log('this.name is now online'); } function Admin(...args) { User.apply(this, args); this.title = 'admin'; } const admin1 = new Admin('admin@admin.com', 'josh'); Admin.prototype.deleteUser = function() { console.log('deleted user'); } Admin.prototype = Object.create(User.prototype); const admin2 = new Admin('admin2@admin.com', 'barry'); console.log(admin1); // 可访问deleteUser,无法访问login方法 console.log(admin2); // 可访问login方法,无法访问deleteUser
核心成因
这个现象本质是混淆了修改原型对象内容和重写构造函数prototype指向两种操作的区别,结合JS原型绑定的基础规则就能完全解释:
- 最核心的基础规则:用
new 构造函数()创建实例时,实例内部的隐式原型[[Prototype]](俗称__proto__)会在创建瞬间,直接指向当下构造函数prototype属性对应的那个对象。这个绑定是固定的引用指向,后续如果构造函数的prototype被改成指向另一个全新对象,已经创建好的实例的隐式原型不会跟着更新,还是会指向最初绑定的旧对象。 - 给
Admin.prototype新增deleteUser方法时,没有改变Admin.prototype的引用指向,只是在它原本指向的旧原型对象上新增了一个属性。此时admin1已经创建完成,它的隐式原型本来就指向这个旧原型对象,因为二者指向同一块内存地址,旧对象上新增的方法admin1自然可以直接访问到。 - 执行
Admin.prototype = Object.create(User.prototype)时,相当于直接给Admin的prototype属性赋值了一个刚创建的全新对象,这个新对象的原型链关联到User.prototype,但本身根本没有之前添加的deleteUser方法:- admin1是这行代码执行前创建的实例,隐式原型仍然指向最开始的旧原型对象,旧对象上保留了之前添加的deleteUser方法,但这个旧对象从来没有和User.prototype建立过原型链关联,自然找不到User原型上的login方法。
- admin2是这行代码执行后才创建的实例,隐式原型指向刚替换的新原型对象,顺着原型链能访问到User.prototype上的login方法,但这个新对象上从未定义过deleteUser方法,自然访问不到。
正确的原型继承写法要注意顺序:必须先通过
Object.create重写构造函数的prototype完成原型链关联,再往新的prototype上添加构造函数自身的方法,顺序写反就会出现示例里的异常。
内容的提问来源于stack exchange,提问作者caveman418
相关产品推荐
相关产品推荐

