众所周知,JavaScript是一门面向对象的操作语言,而我们想要用JavaScript对象化写法的时候,不得不提出一个操作符,叫做new操作符,那么不用new操作符和用new操作符有什么区别呢?
首先,我们去看下new在JavaScript里面的用法, 按照javascript语言精粹中所说,如果在一个函数前面带上new来调用该函数,那么将创建一个隐藏连接到该函数的prototype成员的新对象,同时this将被绑定到那个新对象上。 这句话说得很抽象,我们根据代码来理解。
function foo(){ this.name = "John"; this.age = 23; var born = 1993; return this.age; } //没用new关键字 var foo2 = foo(); console.log(foo2);//23 //用new关键字 var foo3 = new foo(); console.log(foo3);//foo {name: "John", age: 23}
通过上面的代码可以明显看出区别,当我们不用new关键字的时候我们只是把foo函数运行了一遍,如果有返回值就就获得这个返回值,没有返回值就输出underfind,只是简单的函数运行一遍而已。但是当我们用new关键字的时候,我们就可以看到不同了,new关键字是生成一个对象,并且生成的对象里面的属性是foo函数公有属性(即只有用this关键字定义的变量),而忽略掉私有变量(即用var定义的变量)和函数返回值,这样上面关于new关键字的解释就好理解了,即通过new操作符创建一个连接到foo函数的对象,对象里包含foo函数里面所有属性和方法。这就是new关键字的作用,并且this是指向我们当前的对象foo3.
js语言没有类这个概念,所以提出用原型来代替类来实现js面向对象写法。js规定每个函数都具有prototype对象,并且prototype对象在函数外部是可以访问的,可以通过"prototype.xxx"来为当前函数增加属性和方法,所以prototype属性可以理解为当我们想为这个函数添加属性或者方法时我们可以在prototype属性下面添加,而不需要去改变构造函数的代码。
function foo(){ this.name = "John"; this.age = 23; var born = 1993; return this.age; } foo.prototype.ccc = function(){ console.log(this.name); } console.log(foo.prototype);//Object {}
chrome的运行结果如下图:
可以看到foo函数里的prototype是可以访问的,并且我们所添加的函数是在prototype对象里的。这样,我们就可以通过prototype属性向对象添加属性和方法。
在上面的我们看到chrome浏览器运行结果的图中,我们打印foo函数prototype对象可以发现里面包含有constructor属性,所以constructor属性的是prototype对象下的一个属性,即:
function foo(){ this.name = "John"; this.age = 23; var born = 1993; return this.age; } console.log(foo.prototype.constructor == foo);//true
可以看到原型对象下的constructor事指向当前构造函数的,函数在创建时就会自动生成constructor属性来指向当前对象,实例化对象时constructor属性也会一并继承到新的对象下面。
__proto__属性当我们通过new关键字构造函数实例化创建一个对象时,这个对象里面含有__proto__属性,__proto__属性指向构造函数prototype属性以及prototype属性下面的对象。即:
function foo(){ this.name = "John"; this.age = 23; var born = 1993; return this.age; } foo.prototype.ccc = function(){ console.log(this.name); } var foo2 = new foo(); console.log(foo2.__proto__ == foo.prototype)//true console.log(foo2.__proto__)//Object {ccc:function(),constructor: foo()}
console.log(foo2.__proto__)在chrome里的运行结果如下图:
可以看到__proto__对象是等于foo.prototype对象的,并且foo.prototype对象下面的方法和属性都已经在实例化对象foo2下的__proto__下面了。
js没有其他面向对象编程语言的多态的概念,所以如何实现多态的概念。这时,js提出了原型链的概念,通过原型链来实现扩展方法以及属性的功能。看如下代码
function foo(){ this.name = "John"; this.age = 23; var born = 1993; return this.age; } foo.prototype.ccc = function(){ console.log(this.name); } var foo2 = new foo(); foo2.ccc();//John var foo3 = new foo(); foo3.ccc();//John //修改foo3对象下面ccc方法 foo3.ccc = function(){ console.log(this.age); } foo3.ccc();//23
从上面代码实例化对象时新的对象不仅克隆了构造的函数属性和方法,也克隆了构造函数原型下的属性以及方法,在foo3对象中,当我们没有修改ccc方法时,函数会在当前对像下查找,没查找到就会去原型对象里查找该方法,在原型对象里找到了ccc方法,打印出this.name为“John”,当我们重写了ccc方法时,ccc方法就已经foo3当前对象下面了,所以在当前对象下面找到了ccc方法,运行ccc方法,并且停止查到,不会再继续向下查,所以js原型链会遵循就近查找原则,如果查找不到当前方法会到原型下去查找,查找不到再去原型下面的原型去查找,直到查找到所有原型为止。我们答应下foo3对象,console.log(foo3),在chrome运行结果如下图:
可以看出foo3对象下面拥有一个ccc方法,并且在原型对象下面也拥有一个ccc方法,遵循就近查找原则,会找到foo3对象下面的ccc方法,而不会去查到原型对象里的ccc方法。所以通过js的原型链可以修改对象的属性和方法。