// 字面量方式建立对象 var person1 = { name: "xyc", age: 23, sayHi: function() { console.log(name); } }; // Object方式建立对象 var person2 = new Object(); person2.name = "lxy"; person2.age = 18; person2.sayHi = function() { console.log(person2.name); }
虽然Object构造函数或对象字面量均可以用来建立单个对象,但这些方式有个明显的缺点:使用同一个接口建立不少对象,会产生大量的重复代码,如上面的代码,每建立一个相似的person对象,就会重复上面的写法,代码较为冗余javascript
为了解决这个问题(代码重复
),下面引入工厂模式 ==>java
function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function(){ alert(this.name); }; return o; } var person1 = createPerson("Nicholas", 29, "Software Engineer"); var person2 = createPerson("Greg", 27, "Doctor");
通俗的解释:工厂模式就是利用了函数的封装调用,类比工厂材料==>成品的过程,完成入口参数==>对象的过程,函数能够无数次的生成,所以可以避免上面产生大量重复代码的状况。工厂模式虽然解决了建立多个类似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)app
为解决这个问题(对象识别
),下面引入构造函数模式函数
构造函数的返回值:
有一个默认的返回值,新建立的对象(实例),当手动添加返回值后(return语句):
(1)返回值是基本数据类型-->真正的返回值仍是那个新建立的对象(实例)
(2)返回值是复杂数据类型(对象)-->真正的返回值是这个对象this
function foo() { var f2 = new foo2(); console.log(f2); // {a: 3} console.log(this); // window return true; } function foo2() { console.log(this); // foo2类型的对象 不是foo2函数 return {a: 3}; } var f1 = foo(); console.log(f1); // true
使用new操做符调用构造函数会经历下面几个步骤:
(1)建立一个以这个函数为原型的空对象.
(2)将函数的 prototype 赋值给对象的 proto 属性
(3)将对象做为函数的 this 传进去。若是有 return 出来东西是对象的话就直接返回 return 的内容,没有的话就返回建立的这个对象prototype
function NewFunc(func){ var ret = {}; if (func.prototype !== null) { ret.__proto__ = func.prototype; } var ret1 = func.apply(ret, Array.prototype.slice.call(arguments, 1)); if ((typeof ret1 === "object" || typeof ret1 === "function") && ret1 !== null) { return ret1; } return ret; }
普通函数的做用主要是封装做用,可以在做用域内多处调用而已code
建立自定义的构造函数意味着能够经过 instanceof 将它的实例标识为一种特定的类型对象
function Person(name,age,job){ this.name = name; this.age = age; this.sayHi = function(){ console.log(this.name); } } var person1 = new Person('xyc', 23); var person2 = new Person('lxy', 22); console.log(person1 instanceof Person); //true console.log(person2 instanceof Person); //true console.log(person1 instanceof Object); //true 由于Person继承自Object,因此这里同样成立.
构造函数建立对象的方式解决了代码重复
和对象识别
的问题,可是建立的对象中含有方法时,每实例化一个Person,就会产生一个方法,也就是一个对象,每一个对象分别占据内存。所以,构造函数建立对象的方式存在内存大量占用的风险
继承
利用原型共享的特性,下面引入原型模式接口
function Person(){} Person.prototype = { constructor: Person, name : "Nicholas", age : 29, job : "Software Engineer", friends : ["Shelby", "Court"], sayName : function () { alert(this.name); } }; var person1 = new Person(); var person2 = new Person(); person1.sayName(); //"Nicholas" person2.sayName(); //"Nicholas" person1.friends.push("Van"); alert(person1.friends); //"Shelby,Court,Van" alert(person2.friends); //"Shelby,Court,Van" alert(person1.friends === person2.friends); //true
原型建立对象的方式将属性和方法都存在与原型中,也就是说,只要经过这种形式建立的对象都会共享这些属性和对象,相对于方法共享这是咱们乐于看到的,可是属性共享让每一个实例缺失了“个性”;另外对于引用类型的属性共享时,如上面的例子,多个实例对引用类型的操做会被篡改
。
实例通常都要有属于本身的所有属性
,这也决定了原型建立方式的局限性。下面引入很是经典的对象建立方式
组合构造函数模式与原型模式:构造函数模式用于定义实力属性,而原型模式用于定义方法和共享的属性
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"]; } Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); } } var person1 = new Person("Nicholas", 29, "Software Engineer"); var person2 = new Person("Greg", 27, "Doctor"); person1.friends.push("Van"); alert(person1.friends); //"Shelby,Count,Van" alert(person2.friends); //"Shelby,Count" alert(person1.friends === person2.friends); //false alert(person1.sayName === person2.sayName); //true
这种方式建立的实例对象,每一个实例都会有本身的一份实例属性的副本,但同时又共享着对方法的引用,最大限度地节省了内存;另外,这种方式还支持相构造函数传递参数,解决了上面的各类问题