Web/03-JavaScript基础/29-原型链.md
2019-06-15 21:00:07 +08:00

238 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 常见概念
- 构造函数
- 构造函数-扩展
- 原型规则和示例
- 原型链
- instanceof
## 构造函数
任何一个函数都可以被newnew了之后就成了构造方法。
如下:
```javascript
function Foo(name, age) {
this.name = name;
this.age = age;
//retrun this; //默认有这一行。new一个构造函数返回一个对象
}
var fn1 = new Foo('smyhvae', 26);
var fn2 = new Foo('vae',30); //new 多个实例对象
```
与普通函数相比,构造函数有以下明显特点:
- 用new关键字调用。
- 不需要用return显式返回值的默认会返回this也就是新的实例对象。
- 建议函数名的首字母大写,与普通函数区分开。
参考链接:
- [JavaScript中的普通函数与构造函数](http://www.cnblogs.com/SheilaSun/p/4398881.html)
当new之后this会先变成一个空对象然后通过`this.name = name`来赋值。
### 构造函数的扩展
![](http://img.smyhvae.com/20180306_1633.png)
上图中发现数组、对象、函数也有构造函数它们的构造函数是Array、Object、funtion。实际开发中都推荐前面的书写方式。
## 原型规则
原型规则是学习原型链的基础。原型规则有五条,下面来讲解。
### 规则1
所有的引用类型(数组、对象、函数),都具有对象特性,都可以**自由扩展属性**。null除外。
举例:
![](http://img.smyhvae.com/20180306_1651.png)
### 规则2
所有的**引用类型**(数组、对象、函数),都有一个`_proto_`属性,属性值是一个**普通的对象**。`_proto_`的含义是隐式原型。
![](http://img.smyhvae.com/20180306_1656.png)
其实规则2是规则1的特例只不过js语法帮我们自动加了 规则2。
### 规则三
所有的**函数**(不包括数组、对象),都有一个`prototype`属性,属性值是一个**普通的对象**。`prototype`的含义是**显式原型**。(实例没有这个属性)
![](http://img.smyhvae.com/20180306_1659.png)
### 规则四
所有的**引用类型**(数组、对象、函数),`_proto_`属性指向它的**构造函数**的`prototype`值。
![](http://img.smyhvae.com/20180306_1701.png)
总结:以上四条,要先理解清楚,然后再来看下面的第五条。
### 规则五
当试图获取一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的`_proto_`中寻找(即它的构造函数的`prototype`)。
`举例代码1`
```javascript
//创建方法
function Foo(name) {
this.name = name;
}
Foo.prototype.alertName = function () {// 既然 Foo.prototype 是普通的对象,那也允许给它添加额外的属性 alertName
console.log(this.name);
}
var fn = new Foo('smyhvae');
fn.printName = function () {
console.log(this.name);
}
//测试
fn.printName(); //输出结果smyhvae
fn.alertName(); //输出结果smyhvae
```
上方代码中,虽然 alertName 不是 fn 自身的属性,但是会从它的构造函数的`prototype`里面找。
**扩展:**遍历循环对象自身的属性
我们知道,`for ... in`循环可以遍历对象。针对上面的那个fn对象它自身有两个属性`name`、`printName`,另外从原型中找到了第三个属性`alertName`。现在如果我们对fn进行遍历能遍历到两个属性还是三个属性呢
答案:两个。因为,**高级浏览器中已经在 `for ... in`循环中屏蔽了来自原型的属性。但是,为了保证代码的健壮性,我们最好自己加上判断**,手动将第三个属性屏蔽掉:
```javascript
for (var item in fn) {
if (fn.hasOwnProperty(item)) {
console.log(item);
}
}
```
## 原型链
还是拿上面的``举例代码1``举例,如果此时在最后面加一行代码:
```
fn.toString(); //去 fn._proto_._proto_ 中查找 toString()方法
```
上面的代码中fn直接调用了 toString()方法,这是因为它通过**原型链**,去`_proto_`的`_proto_`里找到了`Object`,而`Object`是由`toString()`方法的。
### instanceof
格式:
```javascript
对象 instanceof 构造函数
```
`instanceof`的作用:用于判断**引用类型**属于哪个**构造函数**。
例1判断一个变量是否为数组 `变量 instanceof Array`
例2
```javascript
function Person(){
}
//p--->Person.prototype--->Object.prototype--->null
var p = new Person();
//构造函数的**原型**是否在 p 对象的原型链上!
console.log(p instanceof Person);
```
例3
```javascript
fn instanceof Foo
```
上面这句话,判断逻辑是:**fn 的`_proto_`一层一层往上找,看能否对应到 Foo.prototype**。
原型链如下:(重要)
![](http://img.smyhvae.com/20180306_1853.png)
注意Object这个构造方法的显式原型是null这是一个特例。
## 常见题目
- 如何准确判断一个变量时数组类型
- 写一个原型链继承的例子
- 描述 new 一个对象的过程
- zepto(或其他框架)源码中如何使用原型链
下面分别讲解。
### 题目一:如何准确判断一个变量时数组类型
答案:
```javascript
var arr1 = [];
console.log(arr1 instanceof Array); //打印结果true。
console.log(typeof arr1); //打印结果object。提示typeof 方法无法判断是否为数组
```
上方代码表明,只能通过 instanceof 来判断是否为数组。而 typeof 的打印结果是 object。
### 题目二:写一个原型链继承的例子
来看个基础的代码:
![](http://img.smyhvae.com/20180306_1931.png)
上面这个例子是基础,但是,在回答面试官的问题时,不要写上面的例子。要写成下面这个例子:(更贴近实战)
**举例:**写一个封装DOM查询的例子
> 这个例子有点像 jQuery 操作DOM节点。
表示这个例子,略难。
### 题目三:描述 new 一个对象的过程
1创建一个新对象
2this 指向这个新对象
3执行代码对this 赋值)
4返回this
参考链接:
- [原型、原型链、继承模式](https://my.oschina.net/u/2600761/blog/1524617)