原型
每一种数据类型都会有一些其独立的与其他类型不同的属性,这些独立的属性被称作为原型。
拿 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"