Js基础知识(二) - 原型链与继承精彩的讲解

2018-02-03 10:35:46来源:segmentfault作者:zhuqitao人点击

分享
作用域、原型链、继承与闭包详解
你真的了解作用域、原型链与继承以及闭包吗?

几个面试常问的几个问题,你是否知道instanceof的原理
如何准确判断变量的类型
如何写一个原型链继承的例子
描述new一个对象的过程
也许有些同学知道这几个问题的答案,就会觉得很小儿科,如果你还不知道这几个问题的答案或者背后所涉及到的知识点,那就好好看完下文,想必对你会有帮助。先不说答案,下面先分析一下涉及到的知识点。

什么是构造函数

JavaScript没有类的概念,JavaScript是一种基于对象的语言,除了五中值类型(number boolean string null undefined)之外,其他的三种引用类型(object、Array、Function)本质上都是对象,而构造函数其实也是普通的函数,只是可以使用构造函数来实例化对象。


事实上,当任意一个普通函数用于创建一类对象时,它就被称作构造函数。像js的内置函数Object、Array、Date等都是构造函数。

在定义构造函数有以下几个特点:

以大写字母开头定义构造函数
在函数内部对新对象(this)的属性进行设置
返回值必须是this,或者其它非对象类型的值

下面定义一个简单的、标准的构造函数:


function Obj(){
this.name = 'name'
return this // 默认有这一行
}
var foo = new Obj() // 使用上面定义的构造函数创建一个对象实例
原型特性

js原型有5个特点,记住这5条特点,相信你一定会弄明白长期困扰你的原型关系。

除了null所有引用类型(Object、Array、Function)都有对象特性,也就是都可以自由扩展属性。
所有引用类型都有一个_proto_属性(又称为:隐式属性),_proto_是一个普通的对象
所有的函数都有一个prototype属性(又称为:显式属性),也是一个普通对象
所有的引用类型的_proto_属性指向它的构造函数的prototype属性(比如:obj._proto_指向Object.prototype,obj是定义的一个普通对象,Object是js的内置函数)
当从一个对象中获得某个属性时,如果这个对象没有该属性,就会去它的_proto_(也就是它的构造函数的prototype)中去寻找

先来解释一下这几条:
第一条的自由扩展性可以通过一个简单的例子来看


var obj = {}
obj.name = 'name'
console.log(obj) // {name:'name'}

第二条和第三条是javascript就是这么规定的,没什么好说的


第四条可以这么理解,当定义一个引用类型的变量var obj = {} 其实是var obj = new Object()的语法糖,这样Object就是obj的构造函数,根据第4条规定,obj._proto_ === Object.prototype,如果不理解可以看看上一章我们讲的js内置函数和上面讲的构造函数


第五条应该好理解,当从obj中获取某个属性时,如果obj中没有定义该属性,就会逐级去它的_proto_对象中去寻找,而它的_proto_指向Object的prototype,也就是从Object的prototype对象中去寻找。


原型链与继承
如果上面明白了原型,那么原型链就会很好理解

根据原型定义的第4条和第5条,很容易发现通过对象的_proto_和函数的prototype把我们变量和构造函数(自定义的构造函数以及内置构造函数)像链子一样链接起来,所以又叫他原型链。


有了原型链,就有了继承,继承就是一个对象像继承遗产一样继承从它的构造函数中获得一些属性的访问权。从下面一个小例子理解:


function Foo (name) {
this.name = name
// return this 默认有这一行
}
Foo.prototype.somefn = function () {
alert(this.name)
}
var bar = new Foo('bar')
bar.somefn()

上面例子中在Foo构造函数的prototype中自定义一个somefn函数。然后通过new Foo()创建一个对象实例并赋值给bar变量,此时bar就等于{name:'bar'}。然后bar.somefn就去bar对象中寻找somefn这个属性,发现找不到,然后就去它的_proto_(其实就是Foo的prototype)中寻找,发现somefn就在Foo的prototype中定义了,就可以愉快的调用并执行somefn了。


这里其实就是一个原型链与继承的典型例子,开发中可能构造函数复杂一点,属性定义的多一些,但是原理都是一样的。


留一个问题,根据上面例子,如果执行bar.stString(),应该去哪里找toString这个方法? (提示:prototype也是普通对象,也有自己的_proto_)


解答一下最一开始提出的问题

看到这里应该对原型链与继承的原理有所了解了,再回头看上面的问题,你也会发现这都是小儿科。
第一个问题:instanceof原理?


var arr = []
arr instanceof Array

instanceof原理就是利用了原型链,当执行arr instanceof Array时,会从arr的_proto_一层一层往上找,看是否能不能找到Array的prototype。
我们知道var arr = [] 其实是var arr = new Array()的语法糖,所以arr的_proto_指向Array的prototype,结果返回true


第二个问题:如何准确判断变量类型?
可以使用instanceof帮助我们判断,而不是typeof


第三个问题可以参考原型链与继承中举的例子


第四个问题:描述new一个对象的过程

创建一个新的对象,
获得构造函数的prototype属性,并把prototype赋值给新对象的_proto_,this指向这个新对象
执行构造函数,返回构造函数的内容

微信扫一扫

第七城市微信公众平台