Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

理解 JavaScript 中的原型 #11

Open
huangtengfei opened this issue Dec 17, 2015 · 0 comments
Open

理解 JavaScript 中的原型 #11

huangtengfei opened this issue Dec 17, 2015 · 0 comments

Comments

@huangtengfei
Copy link
Owner

prototype 与 proto 的联系与区别

重要的话放前面讲。首先,理解一个函数的原型属性 prototype 其实和实际的原型 __proto__ 没有关系至关重要。

prototype 只有在 Function 中有,而 __proto__ 在 Function 和 Object 中都有。

关于Function

在 JavaScript 中,除了五种原始类型(BooleanNumberStringNullUnderfined),引用类型包括 ArrayObjectRegExpFunctionDateError等,但从根本上只有两种,即 Function 和 Object,其他的引用类型都是从 Object 衍生出来。

对于这两种类型的判断,前者(原始类型)判断用 typeof,后者(引用类型)用 instanceof

var a = true;
typeof a;   // boolean

var b = {};
typeof b;   // object
b instanceof Object;    // true

var c = [];
typeof c;   // object
c instanceof Object;    // false
c instanceof Array;     // true

在 js 中,Function 和 Object 是平级的,而且 Function 能做但 Object 做不了的事情:

第一,Fuction 可以被执行。

第二,Fuction 可以被当做 Object 的构造函数。当使用 new 操作符后面跟着一个 Function 类型的变量时,这个 Function 变量会被当成构造函数返回一个 Object 对象。

function Foo(){
    ...
}
var foo = new Foo();

第三,Function 有内置的 prototype 属性,而 Object 没有。正是因为有了把 Function 当做构造函数的功能,才需要 prototype 属性。

关于继承(proto

继承仅限于引用类型,即 Function 和 Object。JavaScript中的继承是基于原型链来实现的,用来明确继承关系的就是 __proto__ ,毕竟只有它同时存在于两者。

用最简单的话来描述javascript中继承的本质:一个对象A的 __proto__ 属性所指向的那个对象B就是它的原型对象(或者叫上级对象、父对象),对象A可以使用对象B中定义的属性和方法,同时也可以使用对象B的原型对象C的属性与方法,以此递归,这也就是所谓的原型链。

下面是一个对象继承的栗子。

var A = {
    name: 'huangtengfei'
}
var B = {
    github: 'github.com/huangtengfei'
}
var C = {
    blog: 'huangtengfei.com'
}

B.__proto__ = C;
A.__proto__ = B;

console.log(A.name);    // huangtengfei
console.log(A.github);  // github.com/huangtengfei
console.log(A.blog);    // huangtengfei.com

prototype的作用

既然 __proto__ 一个东西把继承问题就都解决了,那要 prototype 做甚?

并不,因为 __proto__ 并非官方标准中定义的属性,当把一个Function当做构造函数使用时,继承关系需要借助 prototype 实现。

举个栗子:

function Foo(name, github, blog){
    this.name = name;
    this.github = github;
    this.blog = blog;
}

Foo.prototype.aboutMe = function(){
    var info = 'my name is ' + this.name +
        '. my github is ' + this.github + 
        '. my blog is ' + this.blog;
    console.log(info);
}

var foo = new Foo('htf', 'github.com/huangtengfei', 'huangtengfei.com');
foo.aboutMe();  
// my name is htf. my github is github.com/huangtengfei. 
// my blog is huangtengfei.com

这是个典型的构建构造函数,然后创建对象的过程。那么,在 var foo = new Foo() 这一步发生了什么呢?

第一步,Foo 函数被执行。Foo 函数在 foo 的作用域下被执行,所以这里 this 指代的就是 foo,这样 name、github、blog 三个属性才会被当做 foo 的属性被创建。

第二步,将 foo.__proto__ 指向 Foo.prototype 。这才是 javascript 构造函数的精髓所在,之后的原理和 关于继承 部分的代码一样,foo 就继承了 Foo.prototype 中(以及其原型链上)的属性与方法。

console.log(foo.__proto__ === Foo.prototype)    // true

对象的 __proto__ 指向的是创建它的函数的 prototype,因此:

Object.__proto__ === Function.prototype    

结论

proto is the actual object that is used in the lookup chain to resolve methods, etc. prototype is the object that is used to build proto when you create an object with new.

意思就是:

__proto__ 是真正用来查找原型链去获取方法的对象。

prototype 是在用 new 创建对象时用来构建 __proto__ 的对象。

更详细的区别可参考:prototype与__proto__的联系与区别

获取对象原型的方法

ECMA引入了标准对象原型访问器 Object.getPrototype(object),到目前为止只有 Firefox 和 Chrome 实现了此访问器。除了 IE,其他的浏览器支持非标准的访问器 __proto__,如果这两者都不起作用的,我们需要从对象的构造函数中找到的它原型属性。

var a = {}; 

//Firefox 3.6 and Chrome 5 
 Object.getPrototypeOf(a);  // [object Object]   

//Firefox 3.6, Chrome 5 and Safari 4 
a.__proto__;    // [object Object]   

//all browsers 
 a.constructor.prototype;   // [object Object]

任何时候,a.__proto__a.constructor.prototype 是等价的。

constructor 与 prototype

var A = function() {};
A.prototype.constructor == A;   // true
var a = new A();
a.constructor == A;     // true
// (a's constructor property inherited from it's prototype)

图解可参考:js原型链原理看图说明prototype原型

instance of 和原型的关系

如果 a 的原型属于 A 的原型链(即 a.__proto__ === A.prototype),表达式 a instance of A 值为 true。

var A = function() {}   
var a = new A(); 

a.__proto__ == A.prototype;     // true
a instanceof A;     // true

// 改变a的原型指向 
a.__proto__ = Function.prototype;
a instanceof A;     // false

原型的作用

原型的作用当然是继承了。体现在以下两种情况:

一、当多个实例共用一个通用原型的时候,原型对象的属性一旦定义,就可以被多个引用它的实例所继承。栗子可以看 JavaScript 面向对象编程 一文

二、扩展原生构造函数 Function、String 等的功能,使其所有实例都拥有此扩展功能。

比如下面一个栗子,对字符串本身进行指定数目的复制:

String.prototype.times = function(count) {
    // 构造空元素中间用当前字符串连接
    return count < 1 ? '' : new Array(count + 1).join(this);
} 

"hello!".times(3);     // "hello!hello!hello!"; 

参考

理解JavaScript原型
JavaScript探秘:强大的原型和原型链

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant