

## 常见概念

- 构造函数

- 构造函数-扩展

- 原型规则和示例

- 原型链

- instanceof

## 构造函数

任何一个函数都可以被new，new了之后，就成了构造方法。

如下：

```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）创建一个新对象

（2）this 指向这个新对象

（3）执行代码（对this 赋值）

（4）返回this


参考链接：

- [原型、原型链、继承模式](https://my.oschina.net/u/2600761/blog/1524617)

