JavaScript中Dog.prototype两种原型赋值写法的结果差异对比
JavaScript两种原型继承赋值写法的核心区别
在JavaScript实现构造函数原型继承的场景下,Dog.prototype = Object.create(Animal.prototype)是标准的原型链继承实现,而Dog.prototype = {...Animal.prototype}本质是属性浅拷贝,二者执行结果存在以下本质差异:
- 原型链连通性完全不同
Object.create(Animal.prototype)会创建一个空对象,将这个空对象的隐式原型(__proto__)指向Animal.prototype,赋值后Dog.prototype和Animal.prototype存在直接的原型链关联,new Dog() instanceof Animal会返回true,符合继承的原型链判定规则。
展开运算符写法生成的是一个普通新对象,该对象的隐式原型默认指向Object.prototype,和Animal.prototype没有任何原型链绑定关系,赋值后new Dog() instanceof Animal会直接返回false,本质没有实现真正的原型链继承。 - 属性继承的时效性不同
Object.create的写法是建立原型引用关系,赋值完成后如果给Animal.prototype新增、修改属性或方法,所有Dog的实例都可以通过原型链实时访问到这些变更。
展开运算符写法是赋值瞬间的一次性浅拷贝,仅会把执行那一刻Animal.prototype上的自有可枚举属性复制到新对象中,后续对Animal.prototype做的任何修改,Dog的实例都无法感知。 - 可继承的属性范围不同
Object.create的写法依托原型链查找规则,Animal.prototype上的所有属性(包括可枚举/不可枚举属性、赋值后新增的属性),都能被Dog实例访问到。
展开运算符仅会拷贝Animal.prototype上的自有可枚举属性,原型链上的属性、不可枚举属性(比如默认挂载在原型上的不可枚举constructor、通过Object.defineProperty定义的不可枚举成员)都不会被拷贝,Dog实例无法访问这部分内容。 - 属性存储层级不同
Object.create写法下,Animal.prototype上的属性不会成为Dog.prototype的自有属性,访问时是沿原型链向上查找;如果在Dog.prototype上定义同名属性,会形成属性遮蔽,不会直接修改Animal.prototype上的原始值。
展开运算符写法下,所有被拷贝的属性都会直接成为Dog.prototype的自有属性,遍历Dog.prototype时会直接枚举到这部分成员;由于是浅拷贝,拷贝得到的引用类型值和Animal.prototype上的对应属性指向同一块内存地址,直接修改这部分引用值的内容(比如给数组push元素、给对象追加属性),会同步影响Animal.prototype上的对应值。 - 默认constructor指向不同
两种写法都会覆盖Dog.prototype原有的自有constructor属性,未手动修正的前提下:Object.create写法生成的原型对象没有自有constructor,会沿原型链找到Animal.prototype上的constructor,最终指向Animal;展开写法生成的原型对象没有拷贝到不可枚举的constructor属性,会沿自身原型链找到Object.prototype上的constructor,最终指向Object。
function Animal() {} Animal.prototype.say = function() { console.log('animal say') } // 给Animal原型添加不可枚举方法 Object.defineProperty(Animal.prototype, 'breathe', { value: function() { console.log('breathe') }, enumerable: false }) // 写法1:Object.create 实现标准原型继承 function Dog1() {} Dog1.prototype = Object.create(Animal.prototype) const d1 = new Dog1() console.log(d1 instanceof Animal) // 输出 true d1.breathe() // 正常输出 'breathe' // 赋值后给Animal原型新增方法 Animal.prototype.run = function() { console.log('run') } d1.run() // 正常输出 'run' // 写法2:展开运算符浅拷贝 function Dog2() {} Dog2.prototype = {...Animal.prototype} const d2 = new Dog2() console.log(d2 instanceof Animal) // 输出 false d2.breathe() // 抛出类型错误:d2.breathe is not a function d2.run() // 抛出类型错误:d2.run is not a function
内容的提问来源于stack exchange,提问作者Michael K
相关产品推荐
相关产品推荐

