# 原型-原型链

在javascript中,函数可以有属性。 每个函数都有一个特殊的属性叫作原型 prototype

每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为原型链 (prototype chain),

function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );

结果如下:

{
    foo: "bar",
    constructor: ƒ doSomething(),
    __proto__: {
        constructor: ƒ Object(),
        hasOwnProperty: ƒ hasOwnProperty(),
        isPrototypeOf: ƒ isPrototypeOf(),
        propertyIsEnumerable: ƒ propertyIsEnumerable(),
        toLocaleString: ƒ toLocaleString(),
        toString: ƒ toString(),
        valueOf: ƒ valueOf()
    }
}

当函数经过 new 调用时,这个函数就成为了构造函数,返回一个全新的实例对象,这个实例对象有一个 proto 属性,指向构造函数的原型对象。

因此 就有这么一条公式 :

实例的 proto 属性 等于 其构造函数的 prototype 属性

function doSomething(){}
doSomething.prototype.foo = "bar"; 
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; 
console.log( doSomeInstancing );

doSomeInstancing 的 proto 属性就是doSomething.prototype. 但是这又有什么用呢?

当你访问 doSomeInstancing 的一个属性,浏览器首先查找 doSomeInstancing 是否有这个属性, 如果 doSomeInstancing 没有这个属性,然后浏览器就会在 doSomeInstancing.proto (也就是 doSomething.prototype) . 如果 doSomeInstancing 的 proto 有这个属性, 那么 doSomeInstancing 的 proto 上的这个属性就会被使用 否则, 如果 doSomeInstancing 的 proto 没有这个属性, 浏览器就会去查找 doSomeInstancing 的 protoproto ,看它是否有这个属性.

默认情况下所有函数的原型属性的 proto 就是 Object.prototype,如果在这个上面也没有找到这个属性,然后就得出结论,这个属性是 undefined

# constructor 属性

每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。

摘自 MDN

这个怎么理解呢

function A() {

}
var a = new A()
a.constructor===A //true

注意 函数的 prototypeconstructor 指向函数本身

A.protopype.constructor===A

# 图解

原型链

guanxi.png

  • JS 说,我好寂寞。因为 JS 的本源是空的,即:null。

  • JS 说,要有神。所以它通过万能术 proto 产生了 No1 这号神,即:No1.proto == null。

  • JS 说,神你要有自己的想法啊。所以神自己想了个方法,根据自己的原型 prototype 创建了对象 Object,即:Object.prototype == No1; No1.proto == null。于是我们把 prototype 叫做原型,就好比 Object 的原型是神,男人的原型是人类一样,同时 proto 叫做原型链,毕竟有了 proto,对象、神、JS 之间才有联系。这时候 Object.prototype.proto == null。

  • JS 说,神你要有更多的想法啊,我把万能术 proto 借你用了。所以神根据 Object,使用 proto 做了个机器 No2,即 No2.proto == No1,并规定所有的东西,通过 proto 可以连接机器,再找到自己,包括 Object 也是,于是 Object 成为所有对象的原型,Object.proto.proto == No1,然后 String、Number、Boolean、 Array 这些物种也是如此。

  • JS 说,神你的机器好厉害喔!你的机器能不能做出更多的机器啊?神咧嘴一笑:你通过万能术创造了我,我通过自己原型创造了对象。如此,那我造个机器 Function,Function.prototype == No2, Function.proto == No2,即 Function.prototype == Function.proto 吧!这样 No2 就成了造机器的机器,它负责管理 Object、Function、String、Number、Boolean、Array 这几个。

# 题目

  • 题目1
var A = function() {};
A.prototype.n = 1;
var b = new A();
A.prototype = {
  n: 2,
  m: 3
}
var c = new A();

console.log(b.n);
console.log(b.m);

console.log(c.n);
console.log(c.m);

  • 题目2
var F = function() {};

Object.prototype.a = function() {
  console.log('a');
};

Function.prototype.b = function() {
  console.log('b');
}

var f = new F();

f.a();
f.b();

F.a();
F.b();
  • 题目3
var foo = {},
    F = function(){};
Object.prototype.a = 'value a';
Function.prototype.b = 'value b';

console.log(foo.a);
console.log(foo.b);

console.log(F.a);
console.log(F.b);

# 记住

Function.prototype === Function.__proto__

Function.prototype.__proto__ === Object.prototype

Object.prototype.__proto__ === null

Function.prototype === Object.__proto__

# 练习1

function Foo() {
    getName = function () {
        console.log(1)
    };
    return this;
}
Foo.getName = function () {
    console.log(2)
};
Foo.prototype.getName = function () {
    console.log(3)
};
var getName = function () {
    console.log(4)
};
function getName() {
    console.log(5)
}

//请写出以下输出结果:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

# 练习2

Object.prototype.a = 'a';
Function.prototype.a = 'a1';
function Person() {}
var yideng = new Person();
console.log(Person.a);
console.log(yideng.a);
console.log((1..a);
console.log(
  yideng.__proto__.__proto__.constructor.constructor.constructor.constructor
);

总结

所有构造函数都是Function的实例,所有原型对象都是Object的实例除了Object.prototype

推荐文章 JavaScript 世界万物诞生记

更新时间: 12/9/2020, 9:34:18 PM