JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

  1. // 创建了一个函数 也是构造函数 Foo
  2. function Foo(){
  3. this.a = 1
  4. this.b = 2
  5. }
  6. // 构造函数实例化过程 通过 new 会创造属于自己的 this 对象,并在执行完之后返回它
  7. var foo = new Foo()
  8. // 在Foo函数的原型上定义属性
  9. // 尽量不要在 Foo 函数的原型上直接定义 Foo.prototype = {b: 3, c: 4,}这样会直接打破原型链
  10. Foo.prototype.b = 3;
  11. Foo.prototype.c = 4;
  12. // foo.[[Prototype]] 有属性 b 和 c (其实就是 foo.__proto__ 或者 foo.constructor.prototype)
  13. // foo.[[Prototype]].[[Prototype]] 是 Object.prototype
  14. // 最后 foo.[[Prototype]].[[Prototype]].[[Prototype]] 即 Object.prototype.__proto__ 是 null
  15. // 这就是原型链的末尾,即 null (也有一说 Object.prototype 就是原型链的末尾)
  16. // 综上,整个原型链如下:
  17. // {a: 1, b: 2} -> {b: 3, c: 4} -> Object.prototype -> null
  18. console.log(foo.toString === Object.prototype.toString); // true -> 由此可证 Object.prototype 在 foo 的原型链上
  19. console.log(foo.a); // 1
  20. // -> a 是 foo 的自身属性吗?是的,该属性的值为 1
  21. console.log(foo.b); // 2
  22. // -> b 是 foo 的自身属性吗?是的,该属性的值为 2
  23. // 原型上也有一个'b'属性,但是它不会被访问到。
  24. // 这种情况被称为 "属性遮蔽 (property shadowing)"
  25. // 先访问自身有的,自身没有再去访问原型链上的
  26. console.log(foo.c); // 4
  27. // -> c 是 foo 的自身属性吗?不是,那看看它的原型上有没有
  28. // -> c 是 foo.[[Prototype]] 的属性吗?是的,该属性的值为 4
  29. console.log(foo.d); // undefined
  30. // -> d 是 foo 的自身属性吗?不是,那看看它的原型上有没有
  31. // -> d 是 foo.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
  32. // -> d 是 foo.[[Prototype]].[[Prototype]] 的属性吗?不是,那看看它的原型上有没有
  33. // -> foo.[[Prototype]].[[Prototype]].[[Prototype]] 为 null,停止搜索
  34. // 找不到 d 属性,返回 undefined

当继承的函数被调用时,this指向的时当前继承者的对象,而不是继承函数的函数所在的原型对象

  1. var obj = {
  2. name: 'obj',
  3. say: function () {
  4. console.log(this.name);
  5. },
  6. }
  7. // 此时调用 obj.say -> this 指向的是 obj
  8. obj.say() // obj
  9. // newObj 是一个继承 obj 的对象
  10. // 相当于 newObj.[[prototype]] === obj
  11. var newObj = Object.create(obj)
  12. // 创建属于 newObj 的自身属性 name
  13. newObj.name = "newObj"
  14. /**
  15. * -> newObj.say 查找时先查找自身没有
  16. * -> 去原型链上查找 newObj.[[prototype]] 因为它继承于 obj ,所以有 say 函数可执行
  17. *
  18. * -> 调用 newObj.say 时,'this' 指向了 newObj 即 newObj.name
  19. * -> 有因为 newObj 自身有 name 这个属性 , 所以输出 newObj
  20. */
  21. newObj.say() // newObj

JavaScript 原型

在上面说过,在JavaScript中,函数是允许拥有属性的。
所有的函数都会有一个特别的属性prototype

  1. function Foo() { }
  2. console.log(Foo.prototype);

我们打印出来后可以看到这么一个对象

  1. {
  2. constructor: ƒ Foo(),
  3. __proto__: {
  4. constructor: ƒ Object(),
  5. hasOwnProperty: ƒ hasOwnProperty(),
  6. isPrototypeOf: ƒ isPrototypeOf(),
  7. propertyIsEnumerable: ƒ propertyIsEnumerable(),
  8. toLocaleString: ƒ toLocaleString(),
  9. toString: ƒ toString(),
  10. valueOf: ƒ valueOf()
  11. }
  12. }

现在我们来给 Foo的原型对象加点自己的私有私货

  1. function Foo() { }
  2. Foo.prototype.lang = 'JavaScript'
  3. console.log(Foo.prototype);

可以看到输出后内容为

  1. {
  2. lang: "JavaScript",
  3. constructor: ƒ Foo(),
  4. __proto__: {
  5. constructor: ƒ Object(),
  6. hasOwnProperty: ƒ hasOwnProperty(),
  7. isPrototypeOf: ƒ isPrototypeOf(),
  8. propertyIsEnumerable: ƒ propertyIsEnumerable(),
  9. toLocaleString: ƒ toLocaleString(),
  10. toString: ƒ toString(),
  11. valueOf: ƒ valueOf()
  12. }
  13. }