JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
// 创建了一个函数 也是构造函数 Foo
function Foo(){
this.a = 1
this.b = 2
}
// 构造函数实例化过程 通过 new 会创造属于自己的 this 对象,并在执行完之后返回它
var foo = new Foo()
// 在Foo函数的原型上定义属性
// 尽量不要在 Foo 函数的原型上直接定义 Foo.prototype = {b: 3, c: 4,}这样会直接打破原型链
Foo.prototype.b = 3;
Foo.prototype.c = 4;
// foo.[[Prototype]] 有属性 b 和 c (其实就是 foo.__proto__ 或者 foo.constructor.prototype)
// foo.[[Prototype]].[[Prototype]] 是 Object.prototype
// 最后 foo.[[Prototype]].[[Prototype]].[[Prototype]] 即 Object.prototype.__proto__ 是 null
// 这就是原型链的末尾,即 null (也有一说 Object.prototype 就是原型链的末尾)
// 综上,整个原型链如下:
// {a: 1, b: 2} -> {b: 3, c: 4} -> Object.prototype -> null
console.log(foo.toString === Object.prototype.toString); // true -> 由此可证 Object.prototype 在 foo 的原型链上
console.log(foo.a); // 1
// -> a 是 foo 的自身属性吗?是的,该属性的值为 1
console.log(foo.b); // 2
// -> b 是 foo 的自身属性吗?是的,该属性的值为 2
// 原型上也有一个'b'属性,但是它不会被访问到。
// 这种情况被称为 "属性遮蔽 (property shadowing)"
// 先访问自身有的,自身没有再去访问原型链上的
console.log(foo.c); // 4
// -> c 是 foo 的自身属性吗?不是,那看看它的原型上有没有
// -> c 是 foo.[[Prototype]] 的属性吗?是的,该属性的值为 4
console.log(foo.d); // undefined
// -> d 是 foo 的自身属性吗?不是,那看看它的原型上有没有
// -> d 是 foo.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// -> d 是 foo.[[Prototype]].[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
// -> foo.[[Prototype]].[[Prototype]].[[Prototype]] 为 null,停止搜索
// 找不到 d 属性,返回 undefined
当继承的函数被调用时,this
指向的时当前继承者的对象,而不是继承函数的函数所在的原型对象
var obj = {
name: 'obj',
say: function () {
console.log(this.name);
},
}
// 此时调用 obj.say -> this 指向的是 obj
obj.say() // obj
// newObj 是一个继承 obj 的对象
// 相当于 newObj.[[prototype]] === obj
var newObj = Object.create(obj)
// 创建属于 newObj 的自身属性 name
newObj.name = "newObj"
/**
* -> newObj.say 查找时先查找自身没有
* -> 去原型链上查找 newObj.[[prototype]] 因为它继承于 obj ,所以有 say 函数可执行
*
* -> 调用 newObj.say 时,'this' 指向了 newObj 即 newObj.name
* -> 有因为 newObj 自身有 name 这个属性 , 所以输出 newObj
*/
newObj.say() // newObj
JavaScript 原型
在上面说过,在JavaScript中,函数是允许拥有属性的。
所有的函数都会有一个特别的属性prototype
function Foo() { }
console.log(Foo.prototype);
我们打印出来后可以看到这么一个对象
{
constructor: ƒ Foo(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
现在我们来给 Foo
的原型对象加点自己的私有私货
function Foo() { }
Foo.prototype.lang = 'JavaScript'
console.log(Foo.prototype);
可以看到输出后内容为
{
lang: "JavaScript",
constructor: ƒ Foo(),
__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}