14前端成长日记-原型与原型链

157 阅读2分钟

原型

每一种数据类型都会有一些其独立的与其他类型不同的属性,这些独立的属性被称作为原型。
拿 Number 类型来说,每一个 Number 类型的数据都有 toFixed,toExponential 这两个属性,那么就可以认为这些属性为 Number 的原型

通过 prototype 可查看数据类型有哪些原型

Number.prototype
//Number {0, constructor: ƒ, toExponential: ƒ, toFixed: ƒ, toPrecision: ƒ, …}
//  constructor: ƒ Number()
//  toExponential: ƒ toExponential()
//  toFixed: ƒ toFixed()
//  toLocaleString: ƒ toLocaleString()
//  toPrecision: ƒ toPrecision()
//  toString: ƒ toString()
//  valueOf: ƒ valueOf()
//  __proto__: Object
//  [[PrimitiveValue]]: 0 

原型链

普通数据类型前加 new 加会创造一个对象,不加 new 会直接创造一个普通类型;比如 new Number() 就会创造一个 Number 对象

var a = new Number(1)
console.log(a)
// Number {1}
//  __proto__: Number
//  [[PrimitiveValue]]: 1

通过上面的代码可以看到变量a有两个属性__proto__和 [[PrimitiveValue]],[[PrimitiveValue]] 的值就是我们给a所赋的值,那么__proto__又是什么呢,它的值为什么是 Number 呢?

__proto__: Number
    constructor: ƒ Number()
    toExponential: ƒ toExponential()
    toFixed: ƒ toFixed()
    toLocaleString: ƒ toLocaleString()
    toPrecision: ƒ toPrecision()
    toString: ƒ toString()
    valueOf: ƒ valueOf()
    __proto__: Object
    [[PrimitiveValue]]: 0

点开__proto__发现它里面的属性和 Number.prototype 里面的属性一模一样

a.__proto__ === Number.prototype //true

再来试试新建一个string

var a = new String('Hello')
console.log(a)
//String {"Hello"}
//  0: "H"
//  1: "e"
//  2: "l"
//  3: "l"
//  4: "o"
//  length: 5
//  __proto__: String
//  [[PrimitiveValue]]: "Hello"
a.__proto__ === String.prototype //true

会发现它的__proto__的值是String,而且__proto__和 String.prototype 也是相等的

所以可以理解成新建的对象是通过__proto__来连接原型并获取其中的属性

通过上面的例子会发现 Number.prototype 和 String.prototype 里面都还有一个__proto__: Object ,那么就可以理解成 Number 和 String 通过__proto__来连接 Object 原型

所以通过__proto__一层一层引用,就是原型链

构造函数的__proto__都指向Function.prototype

Number.__proto__ === Function.prototype //true
String.__proto__ === Function.prototype //true
Object.__proto__ === Function.prototype //true
Array.__proto__ === Function.prototype //true

所有对象的原型链最终都会指向到 Object.prototype 而 Object.prototype 的原型是 null,所以 原型链的终点是 null

constructor

prototype 有一个属性 constructor,这个属性默认指向构造函数

修改原型对象时,一般要同时修改constructor属性的指向。

// 坏的写法
C.prototype = {
  method1: function (...) { ... },
  // ...
};

// 好的写法
C.prototype = {
  constructor: C,
  method1: function (...) { ... },
  // ...
};

// 更好的写法
C.prototype.method1 = function (...) { ... };

如果不能确定constructor属性是什么函数,还有一个办法:通过name属性,从实例得到构造函数的名称。

function Foo() {}
var f = new Foo();
f.constructor.name // "Foo"