Web/04-JavaScript基础/原型链.md
2020-08-24 10:38:12 +08:00

240 lines
6.3 KiB
JavaScript
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)
上图中发现数组对象函数也有构造函数它们的构造函数是ArrayObjectfunction实际开发中都推荐前面的书写方式
## 原型规则
原型规则是学习原型链的基础原型规则有五条下面来讲解
### 规则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这是一个特例
issues 101补充通过原型链查找时如果你找的是一个属性的话则返回 undefined如果你找的是一个方法则报错
## 常见题目
- 如何准确判断一个变量是数组类型
- 写一个原型链继承的例子
- 描述 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)