JavaScript原型与继承的秘密
__proto__
除null
和undefined
,JS中的所有数据类型都有这个属性; 它表示当我们访问一个对象的某个属性时,如果该对象自身不存在该属性, 就从它的__proto__
属性上继续查找,以此类推,直到找到,若找到最后还是没有找到,则结果为undefined
我们把一个对象的__proto__
属性所指向的对象叫该对象的原型
;我们可以修改一个对象的原型
来让这个对象拥有某种属性或某个方法
-
// 修改一个Number类型的值的原型
-
const num =
1;
-
num.__proto__.name =
"My name is 1";
-
console.log(num.name);
// My name is 1
-
-
// 修改一个对象的原型
-
const obj = {};
-
obj.__proto__.name =
"dreamapple";
-
console.log(obj.name);
// dreamapple
需注意的是,__proto__
属性虽多数浏览器支持,但其实它仅在ECMAScript 2015规范
中才被准确定义, 目的是为了给这个传统的功能定制一个标准,以确保浏览器间的兼容性。通过使用__proto__
属性来修改一个对象的原型非常慢且影响性能。 所以,若想获取一个对象的原型,推荐用Object.getPrototypeOf
或Reflect.getPrototypeOf
,设置一个对象的原型推荐用Object.setPrototypeOf
或Reflect.setPrototypeOf
prototype
首先要记住的是,该属性一般只存在于函数对象上; 只要是能作为构造器的函数,都包含这个属性。即只要这个函数能通过new
生成一个新对象, 那么这个函数肯定具有prototype
属性。因为我们自定义的函数都可通过new
生成一个对象,所以我们自定义的函数都有prototype
这个属性
-
// 函数字面量
-
console.log((
function(){}).prototype);
// {constructor: ƒ}
-
-
// Date构造器
-
console.log(
Date.prototype);
// {constructor: ƒ, toString: ƒ, toDateString: ƒ, toTimeString: ƒ, toISOString: ƒ, …}
-
-
// Math.abs 不是构造器,不能通过new操作符生成一个新的对象,所以不含有prototype属性
-
console.log(
Math.abs.prototype);
// undefined
prototype
属性有什么作用呢?作用就是:函数通过new
生成的一个对象, 这个对象的原型(__proto__
)指向该函数的prototype
属性:
-
// 其中F表示一个自定义的函数或者是含有prototype属性的内置函数
-
new F().__proto__ === F.prototype
// true
-
-
// 通过函数字面量定义的函数的__proto__属性都指向Function.prototype
-
(
function(){}).__proto__ ===
Function.prototype
// true
-
-
// 通过对象字面量定义的对象的__proto__属性都是指向Object.prototype
-
({}).__proto__ ===
Object.prototype
// true
-
-
// Object函数的原型的__proto__属性指向null
-
Object.prototype.__proto__ ===
null
// true
-
-
// 因为Function本身也是一个函数,所以Function函数的__proto__属性指向它自身的prototype
-
Function.__proto__ ===
Function.prototype
// true
-
-
// 因为Function的prototype是一个对象,所以Function.prototype的__proto__属性指向Object.prototype
-
Function.prototype.__proto__ ===
Object.prototype
// true
constructor
constructor
表示一个对象的构造函数,除null
和undefined
,JS中的所有数据类型都有这个属性; 我们可通过下面的代码来验证一下:
-
null.
constructor
// Uncaught TypeError: Cannot read property 'constructor' of null ...
-
undefined.
constructor
// Uncaught TypeError: Cannot read property 'constructor' of undefined ...
-
-
(
true).
constructor
// ƒ Boolean() { [native code] }
-
(
1).
constructor
// ƒ Number() { [native code] }
-
"hello".
constructor
// ƒ String() { [native code] }
一个对象的constructor
属性确切地说并不是存在这个对象上面的; 而是存在这个对象的原型上(如果是多级继承需手动修改原型的constructor
属性),我们可用下面的代码来解释一下:
-
const F =
function() {};
-
// 当我们定义一个函数的时候,这个函数的prototype属性上面的constructor属性指向自己本身
-
F.prototype.
constructor === F;
// true
对JS的原始类型(string, number, boolean, null, undefined, symbol (new in ECMAScript 2015)),它们的constructor
属性是只读的,不可修改:
-
(
1).
constructor =
"something";
-
console.log((
1).
constructor);
// 输出 ƒ Number() { [native code] }
如果真想改这些原始类型的constructor
属性,也不是不可以:
-
Number.prototype.
constructor =
"number constructor";
-
(
1).
constructor =
1;
-
console.log((
1).
constructor);
// 输出 number constructor
当然上面的方式不推荐
在这里插入代码片