JavaScript基础:构造函数和普通函数的区别,原型,new 的原理,原型链的形成原理

首先我们先来看两个问题:

1,构造函数和普通函数到底有何区别?

2,函数为什么可以打印出名字、形参长度,那么它是对象吗?

 

1,构造函数和普通函数有何区别:

function Test(){} // 构造函数?

function test(){} // 普通函数?

以上2行代码,相信很多人一眼就可以看出,Test是构造函数,test是普通函数。

那么依据是什么呢?

你肯定会说,首字母大写当然就是构造函数了。

 

那么请看下图:

JavaScript基础:构造函数和普通函数的区别,原型,new 的原理,原型链的形成原理_第1张图片

可以看到,函数怎么执行和它的名字并没有直接的关系。

 

那么我们知道了:

1,从结构上来说,构造函数和普通函数没有任何区别。

2,构造函数能构造出对象的前提,必须得加 new 执行。有了 new 之后,就能产生构造函数的功能。没 new 就是正常函数执行。

所以,构造函数和普通函数的命名,才需要按照首字母的大小写来区分。

首字母大写就是告诉你,我是一个构造函数,请用 new 来执行。

因为如果不加 new,它们的功能都是一样的。

 

2,函数是对象吗?

JavaScript基础:构造函数和普通函数的区别,原型,new 的原理,原型链的形成原理_第2张图片

从上图可以很明显的看到:函数也可以当作对象来用。

那么接下来,就让我们看看 js 里面的难点,原型:

 

原型:

1,原型是 function 对象上面的一个属性。

2,原型是个对象。(个别例外)

3,通过构造函数产生出来的对象,可以继承该原型的属性和方法。

函数在定义时,系统就会自动创建一个对象作为该函数的原型。函数 prototype 属性的值,就指向该原型。同时该原型 constructor 属性的值也指向该函数。如下图:

JavaScript基础:构造函数和普通函数的区别,原型,new 的原理,原型链的形成原理_第3张图片

 

那么为什么通过 new 之后,就可以继承属性和方法呢,当我们 new 的时候,到底发生了什么?

1,当我们在通过 new 执行一个函数的时候,JS 引擎会在该函数内部的最顶端,隐式的加上 var this = { }。

2,然后依次执行函数内部代码。

3,最后,在函数内部的最下方,隐式的 return this。

function Person (name, age){

    // JS引擎会自动的加上:var this = { }

    this.name = name
    this.age = age
    this.say = function () {
        console.log('我会说话')
    }

    // 最后则会:return this
}
Person.prototype.num = 666

var zhangsan = new Person('张三', 18)

zhangsan.name   // 张三
zhangsan.age    // 18
zhangsan.say()  // 我会说话
zhangsan.num    // 666

如果只是这样,那么如何继承原型上面的属性和方法呢?

那是因为在函数内部的 this 并不仅仅是一个空对象, 实际上是:

var this = Object.create( 原型 ) 

var obj = Object.create( 原型 )

这句话的意思是:创建一个对象 obj,并且指定它的原型是 xxx

例如:

var demo = { name : 'aaa' }

var obj = Object.create(demo)

等同于:

var obj = {
    __proto__: demo
}

也可以理解为:

var this = { __ proto__ :  原型 }

如下图:

JavaScript基础:构造函数和普通函数的区别,原型,new 的原理,原型链的形成原理_第4张图片

 

搞清楚了 new 做的事情,以及为什么可以继承原型上的方法之后,那么原型链也就更好理解了:

原型之间形成的链接结构,也称为原型链。(__proto__ 形成的链式结构)

function Animal(){
    this.name = '我属于动物'
}
Animal.prototype.eat = function () {
    console.log('我会吃东西')
}

var animal = new Animal()

function Dog(){
    this.say = function () {
        console.log('汪汪叫')
    }
}
Dog.prototype = animal

var dog = new Dog()
dog.say()
dog.eat()
dog.name

如下图:

JavaScript基础:构造函数和普通函数的区别,原型,new 的原理,原型链的形成原理_第5张图片

 

所以它的原理其实非常的简单,就三个字:往上找。

首先,从自己身上找, 如果自己身上有,那就直接拿来用。

如果没有,它会顺着 __proto__ 这根线,继续去它爸爸身上找,如果它爸爸身上有,那自然就好。

如果还没有,它还会再去它爷爷身上找。

如果还没有,在往上找,一直找到最后,能找到 Object.prototype 上,这也是它最终的一个归宿。

因为 Object 其实是所有人间接的祖宗,它是最上面的。

一直找到 Object 身上,如果还没有,那就是 undefined。

说白了,就是一个一个往上找,自己没有就找它爸爸,还没有,就找它爷爷,一直找到 Object 身上,还没有,就是 undefined。

你可能感兴趣的:(JavaScript基础)