new一个函数和直接调用的区别
首先说说new()到底做了什么.
1.创建一个新对象.
2.将构造函数的作用域付给新对象(因此this就指向了这个新对象).
3.执行构造函数的代码(为这个新对象添加属性).
4.返回新对象.(如果没有返回值或者函数有返回值,但是该返回值不是对象的话,则返回第一步建立的对象,如果函数没有返回值,且返回值是对象的话,则忽略第一步建立的对象,返回默认返回的对象.)
用栗子说明
1 | function Animal(name){ |
Animal 本身是一个普通函数,但当通过new来创建对象时,Animal就是构造函数。
JS引擎执行这句代码时,在内部做了很多工作,用伪代码模拟其工作流程如下:
1 | new Animal("cat") = { |
(1)创建一个空对象obj;
(2)把obj的proto指向构造函数Animal的原型对象prototype,此时便建立了obj对象的原型链:obj->Animal.prototype->Object.prototype->null
(3)在obj对象的执行环境调用Animal 函数并传递参数“cat”。 相当于var result = obj.Animal(“cat”)。
(4)考察第3步返回的返回值,如果无返回值或者返回一个非对象值,则将obj返回作为新对象;否则会将返回值作为新对象返回。
理解了其运行机制以后,我们知道cat其实就是过程(4)的返回值,因此我们对cat对象的认知就多了一些:
cat的原型链是:cat->Animal.prototype->Object.prototype->null
cat上新增了一个属性:name
分析完了cat的产生过程,我们再看看输出结果:
cat.name -> 在过程(3)中,obj对象就产生了name属性。因此cat.name就是这里的obj.name
cat.color -> cat会先查找自身的color,没有找到便会沿着原型链查找,在上述例子中,我们仅在Animal对象上定义了color,并没有在其原型链上定义,因此找不到。
cat.say -> cat会先查找自身的say方法,没有找到便会沿着原型链查找,在上述例子中,我们在Animal的prototype上定义了say,因此在原型链上找到了say方法。
另外,在say方法中还访问this.name,这里的this指的是其调用者obj,因此输出的是obj.name的值。
对于Animal来说,它本身也是一个对象,因此,它在访问属性和方法时也遵守上述查找规则,所以:
Animal.color -> “black”
Animal.name -> “Animal” , Animal先查找自身的name,找到了name, 但这个name不是我们定义的name,而是函数对象内置的属性。
一般情况下,函数对象在产生时会内置name属性并将函数名作为赋值(仅函数对象)。
Animal.say -> Animal在自身没有找到say方法,也会沿着其原型链查找,话说Animal的原型链是什么呢?
从测试结果看:Animal的原型链是这样的:
Animal->Function.prototype->Object.prototype->null
因此Animal的原型链上没有定义say方法!
在Javascript中, 通过new可以产生原对象的一个实例对象,而这个实例对象继承了原对象的属性和方法。因此,new存在的意义在于它实现了Javascript中的继承,而不仅仅是实例化了一个对象!
再写个测试栗子
1 | function Test() { |
我们可以在分析一下
1 | var newT = new Test(); |
它干了什么
1 | new Test() = { |
重要的是第三点
在obj环境中执行调用Test函数,用result变量承接Test函数的结果.显然这里是个字符串.
第四步:
返回的字符串’Test’不是一个’oobject’类型,则将obj作为返回值返回.
再写个测试栗子
1 | function Test() { |
test1 就单纯的是一个变量承接 一个函数.没什么好说的.
重点再来分析一一下test2.
1 | new Test() = { |
第四步返回的是一个function (){} 是’object’类型.就直接返回result去承接这个变量.注意 不是obj,所以this.name = ‘Test’,是在obj这个对象里.
(Function Array RegExp 也算obj的一种)