ECMAScript没有类的概念,所以面向对象与传统的静态语言有很大不一样。java
建立自定义对象简单的方式有两种:app
var person1 = new Object(); //建立object实例 var person2 = {}; //对象字面量
而后能够给对象动态的添加须要的属性和方法,就能够得到对象的属性,调用对象的方法了。函数
对于这两种建立对象的方式没有什么区别。不过对象字面量表示法更受喜欢,由于代码量少,并且在建立的时候能够一次性将属性和方法定义好:this
1 var person2 = { 2 name:'zhangsan', 3 sayName:function(){ 4 alert('my name is' + this.name); 5 } 6 };
可是这仍是有个问题:若是要建立多个person对象,都有name属性和sayName方法,每建立一个对象都得这么写一次,太麻烦了!spa
好在前辈们已经总结出了几种种建立对象的方式:prototype
1. 工厂模式指针
function createPerson(name){ var o = new Object(); o.name = name; o.sayName = function(){ alert('my name is' + this.name); }; return o; }; var person1 = createPerson('zhangsan'); var person2 = createPerson('lisi'); person1.sayName(); //zhangsan person2.sayName(); //lisi
感受跟调用java方法差很少。虽然比刚开始那种方式好点,但也是优缺点并存:code
优势:解决了建立类似对象的问题,减小代码量。对象
缺点:没法知道一个对象的类型,所有都是Object。blog
2. 构造函数模式
function Person(name, age){ this.name = name; this.age = age; this.sayName = function(){ alert('hello:' + name + ',' + age); } } var person1 = new Person('zhangsan',20); var person2 = new Person('lisi', 30); person1.sayName(); person2.sayName(); console.log(person1 instanceof Person); //true console.log(person2 instanceof Person); //true console.log(person1 instanceof Object); //true console.log(person2 instanceof Object); //true
Person函数的特色:
1.没有显式的建立对象,即没用new 关键字。
2.属性都赋值了给了this对象。
3.没有return.
要建立实例,必须用new关键字。new作了如下四件事情:
1.建立一个新的对象,此时对象就是个空对象。
2.将构造函数的做用域赋值给新对象,此时this就指向刚才那个空对象了。
3.执行Person函数,其实就是在给那个新对象一个一个地动态添加属性。
4.将添加完属性的这个新对象返回,因此person1,person2就指向新对象了。
建立出来的示例既是Person的实例,也是Object的实例。
构造函数终归函数函数,能够被看成普通函数调用,不过this对象指向全局的Global对象(window对象),以后就能够经过window对象来调用了。
//做为普通函数调用 Person('wangwu',30); //属性和方法都会被添加给全局的window对象 window.sayName(); //wangwu
构造函数也能够在另外一个对象的做用域中调用:
//在另外一个对象的做用域中调用 var obj= new Object(); var obj1 = new Object(); Person.call(obj, 'zhaoliu',40); Person.apply(obj1,['zhaoqi',50]); obj.sayHello(); obj1.sayHello();
使用构造函数的方法会有什么问题呢?它将全部的对象的属性和方法都建立了一遍,每一个对象的方法都是一个Function实例。
obj.sayHello == obj1.sayHello; //false
结果是false就论证了这一点。
按道理说属性应该是私有的,方法应该是共享的才对。为了解决这个问题,原型模式该登场了。
3. 原型模式
什么是原型?
js中每一个函数都有prototype(原型)属性,它是一个对象类型的指针。而这个对象包含特定类型的全部实例的共享属性和方法。
既然每一个函数都有prototype属性,并且仍是对象类型的,那么就能够这么使用了:
function Person(){ } Person.prototype.name = 'zhangsan'; Person.prototype.age = 20; Person.prototype.sayHello = function(){ alert('name:' + this.name + ',age:' + this.age); }; var person1 = new Person(); person1.sayHello();
Person函数定义并无什么内容,根据new关键字的特性,因此其实person1实例本身也没什么内容的,这些属性和方法都是从原型中寻找的。
原型有如下几个特征:
1.全部的原型对象(prototype指向的这个对象)都有一个constructor属性,这个属性指向 含有prototype属性 所在函数。Person.prototype表示Person函数的原型对 象,而Person函数有还有prototype这个属性,因此Person.prototype.constructor===Person 成立。
2.当为对象实例添加一个属性时,这个属性会屏蔽原型对象中保存的同名属性,这个属性只是该实例特有的。
3.代码读取某个实例的属性时,先从实例自己搜索,若是找不到,就搜索对应的原型对象。
4.原型具备动态性,每次对原型对象的修改都能当即从实例上反应出来。
第四点能够用代码验证:
function Person(){ } var person1 = new Person(); //调用构造函数,该实例会有一个指向最初原型对象的指针 Person.prototype.sayHello = function(){ alert('hello'); } person1.sayHello(); //ok Person.prototype = { //修改原型对象的指向 constructor:Person, //重写原型对象会使得constructor指向Objec构造函数,所以要动态指定 name:'lisi', age:30, sayName:function(){ alert('name:' + this.name); } }; //person1.sayName(); //Error //由于person1的原型指针仍然指向最初的原型对象,她没有sayName属性 var person2 = new Person(); person2.sayName(); //ok //person2是从新new出来的,它的原型指针指向的是新的原型对象
重写原型对象切断了现有原型与任何以前已经存在对象实例之间的关系,之前的实例仍然引用的是最初的原型。
原型对象的问题:
对全部实例共享,若是原型对象的属是引用类型的属性,若是某一实例修改了该属性,则也会影响其余实例:
解决办法:
结合构造函数和原型,属性私有,方法公有。
寄生式构造感受就是结合和工厂和构造函数模式,new出来的对象还不用,返回一个本身创造的对象。我的感受更接近于工厂模式。
4. 稳妥构造
特征:不用new操做符调用构造函数;建立对象的方法中没有this。
function Person(name, age, job){ var o = new Object(); o.sayName = function(){ alert(name); } //构造函数传入的值没法修改,只能访问 return o; } var person = new Person('zhangsan', '20', 'programer'); person.sayName();
这样传入构造函数的值就不修改,由于它是传入的参数,并无属性把这个值存储起来。