学习js中的原型链
学习!!!!!!
js不是一个面向对象编程语言(妈的,注孤生)。但是其创始人又设计一个原型链这么个鬼玩意来模仿面向对象编程,就很坑爹。
首先先了解一下js中的一个重要概念:所有的对象都是由函数创建的,js中的一切引用类型都是对象,包括Array,Object,Function等。
我看过知乎一个文章:JavaScript 世界万物诞生记。
这边文章中作者用言简意赅的语言就形象的描写了js中的原型链。
在这里我是重复一遍,以自己的语言,自己的理解再加深一遍自己的印象。
学习原型链之前有两个特别重要的概念:
1.原型:prototype。 每一个函数都有一个原型属性。
2.隐式原型:__proto__。每一个对象都有一个隐式原型属性,不过一般用不到。
函数也是对象,所以函数不仅有 原型 ,也有 隐式原型。
js中,所有的对象都是由函数创建的,并且是通过 new Function 的方式创建。
例如:
var obj = {a:1}
这相当于:
var obj = new Object() obj.a = 1
再比如:
var arr = [1,2]
这相当于:
var arr = new Array(1,2)
我们先看一下什么是原型,什么是隐式原型。
在浏览器中我们运行以下代码:
function A(){ this.a = 1 } console.log(A.prototype)
看一下结果:
看的出来,A.prototype 也是一个对象,其中包含 constructor, __proto__等属性。
但是这个 constructor 是干嘛的呢?这个就是构造函数,也就是函数本身。
我们使用上面定义的方法A创建一个对象出来:
var a = new A() console.log(a) console.log(a.__proto__)
看一下浏览器打印出来的结果:
看上图中打印出来的结果,是不是感觉很眼熟,对,没错,跟上面打印出来的 A.prototype 好像,简直一模一样。
那么我们再运行下面的代码:
console.log(a.__proto__ === A.prototype)
看结果:
果然,a.__proto__ 就是 A.prototype。
这其实就是函数创建对象过程中的最后一步:
var a = new A() =====> 其实就是: var a = {} A.call(a) a.__proto__ = A.prototype
这就是js中函数创建对象的过程。
牢记一下四点:
1. 对象的 __proto__ 指向创建该对象的函数的 prototype。
2. 所有对象的 prototype 是一个 Object 类型的对象。
这里有个特殊的例子: 因为函数也是对象,所以函数也是由函数创建,创建函数的函数是 Function,有点绕。
其中Function.prototype === Function.__proto__ 。而且 Function.prototype 是一个 Function 类型的。
3. 所有 Function 类型的对象的 __proto__ 都会指向 Function.prototype,也是一个 Function 类型。
4. 所有 Object 类型的对象的 __proto__ 最终都指向 Object.prototype。
这里也有一个特殊的例子:Object.prototype.__proto__ 是 null。
在js中,String,Number,Boolean等基础类型也有 __proto__ 属性。
var a = 1,b = 'ss',c = true console.log(a.__proto__) console.log(b.__proto__) console.log(c.__proto__)
打印一下结果:
js里面的引用类型常用的有 Array, Object, Function等等。
所以为了区分js中的引用类型,js有一个关键词:instanceof。
typeof 只能区分基础类型和引用类型,对于引用类型更细致划分就无能为力了。而 instanceof 能准确的判断出是什么类型。
function Foo(){} var f1 = new Foo() console.log(f1 instanceof Foo)
浏览器运行一下代码:
这里是 true ,因为 f1 的类型就是 Foo。
介绍一下instanceof的判断机制:
instanceof 两个参数,左边的为 A,右边的为 B。运行时会沿着 A 的 __proto__ 开始查找,如果找到了 B 的 prototype,则返回 true ,若找到了终点(Object.prototype.__proto__)也没有找到,则返回false。
再看上面这段代码,首先判断 f1.__proto__ === Foo.prototype , 这里判断为 true ,直接返回 true。
再来一段代码:
function Foo(){} var f1 = new Foo() console.log(f1 instanceof Object)
看结果:
instanceof 判断时,先判断 f1.__proto__ === Object.prototype ,判断为 false,然后继续判断 f1.__proto__.__proto__ (f1.__proto__是Object类型,Object类型的 __proto__ 都等于 Object.prototype) === Object.prototype,判断为true,返回true。
再来一段代码:
function Foo(){} var f1 = new Foo() console.log(f1 instanceof Function)
看结果:
结果为false。因为 f1 是 Object 类型的。
但是作为一个初学者,还是一步一步剖析一下为什么是false。
首先判断:f1.__proto__ === Function.prototype 判断为false 继续判断 Foo.prototype__proto__ === Function.prototype 判断为false 继续判断 Object.prototype__proto__ === Function.prototype 判断为false 已经到达最顶端 Object.prototype.__proto__ , 返回最后的结果false
再最后看一下 Object 和 Function 的相爱相杀。
console.log(Object instanceof Function) // true , 因为函数类型的 __proto__ 都指向 Function.prototype console.log(Function instanceof Function) // true , 因为 Function.__proto__ 指向 Function.prototype console.log(Function instanceif Object) // true , 因为 Function.__proto__.__proto__ 指向 Object.prototype console.log(Object instanceof Object) //true 因为 Object.__proto__.__proto__ 指向 Object.prototype
上面介绍了 instanceof ,下面介绍一下原型链。
在上面 instanceof 从当前对象的 __proto__ 判断到 Object.prototype.__proto__,原型链也类似于这样,也是一个链子。
当访问一个对象的属性时,先在对象的基本属性中查找,如果没有,再沿着__proto__这条链向上找,这就是原型链。
function Foo(){} var f = new Foo() f.a = 1 Foo.prototype.a = 10 Foo.prototype.b = 20 console.log(f.a) console.log(f.b) console.log(f.c) console.log(f.hasOwnProperty('a')) console.log(f.hasOwnProperty('b'))
看一下结果:
在上图中可以看到 f.a 是 1,f.b 是 20,f.c 是 undefined。
因为对象 f 有自己的属性 a 为 1,所以 f.a 就是 1,所以 f.hasOwnProperty('a') 为 true。但是 f 没有属性 b,所以 f.hasOwnProperty('b') 为 false,只能去 f.__proto__ 也就是 Foo.prototype 中去找属性 b,在这里找到了 b = 20,所以打印出来是20。
至于 f.c ,因为 f 没有属性 c ,Foo.prototype 也没有属性 c ,Foo.prototype.__proto__ 也就是 Object.prototype 也没有属性 c ,所以返回 undefined。
关于原型链,谨记两点:
1. 因为所有对象的原型链都会找到 Object.prototype,因此所有的对象都有 Object.prototype 的方法。
2. 每一个函数都有 call、apply、bind 方法,因为这些方法是 Function.prototype 的方法,所有的函数原型链都会找到 Function.prototype。
以上所有就是我关于js中的原型,原型链的学习和总结。