前端这块,不总结感觉自己都得要学晕。
标准对象模式
var person = new Object(); person.name = "Nicholas"; person.age = 29; person.job = "Software Enginner"; person.sayName = function () { console.log(this.name); }
对象字面量形式
var person = { name: "Nicholas", age: 29, job: "Software Enginner", sayName: function () { console.log(this.name) } }
工厂模式
抽离了创建具体对象的过程,使用函数来封装以特定接口创建对象的细节
优点:可以反复创建相似的对象
缺点:无法解决对象识别的问题function createPerson(name, age, job) { var o = new Object(); o.name = name; o.age = age; o.job = job; o.sayName = function () { console.log(this.name) } return o } var person1 = createPerson("hugo", 18,"webFont"); var person2 = createPerson("hugo2", 18,"webFont2"); console.log(person1, person2); //此时,得到person1 和 person2 都是Object类型 //假如我们继续创建另外一个工厂,用来创建一个动物类。那么它的类型也一定是Object //所以,不管是哪个工厂,创建出来的对象类型居然一致,也就是上面所说的无法解决对象识别的问题
构造函数模式
优点:解决了工厂模式无法解决对象识别的问题, 没有显示的创建对象,直接将属性和方法赋给this对象,没有return对象
缺点:在案例中,每个Person对象都包含一个不同的Function实例的本质,以这种方式创建函数,会导致不同的作用域链和标识符解析,但创建Function新实例的机制仍是相同的。而如果将方法放到全局作用域中,自定义的引用类型就没有封装性可言
new操作具体做了什么(调用构造函数实际操作步骤)?
—1、创建一个新对象
—2、将构造函数的作用域赋值给新对象(this指向该对象)
—3、执行构造函数中的代码(为新对象添加属性)
—4、返回新对象function Person(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = function () { console.log(this.name) } } var person3 = new Person("hugo",18,"webFont"); console.log(person3); //检测对象类型 console.log(person3.constructor == Object); //false console.log(person3.constructor == Person); //true console.log(person3 instanceof Object); //true console.log(person3 instanceof Person); //true var person4 = new Person("hugo2",18,"webFont2"); person4.sayName(); //hugo2 console.log(person3.sayName() == person4.sayName()); //ture //将共有的方法放在外面 function Person2(name, age, job) { this.name = name; this.age = age; this.job = job; this.sayName = sayName2; } function sayName2() { console.log(this.name) } var person11 = new Person('hugo11',18, 'webFont11'); var person22 = new Person('hugo11',18, 'webFont11'); console.log(person11.sayName == person22.sayName); //true //将方法放在外面,存在一个问题,这样会造成全局变量的污染。
原型模式(很少使用)
省略了为构造函数传递初始化参数这一环节,结果所有实例在默认情况下都将取得相同的属性值,需要各自单独传入参数
优点:可以解决构造函数模式创建多个方法实例的问题,可以让所有对象实例共享原型所包含的属性和方法,不必在构造函数中定义对象实例的信息,而可以直接将信息添加到原型对象中
function Person3() { } Person3.prototype.name = "hugo"; Person3.prototype.age = 18; Person3.prototype.sayName = function () { console.log(this.name) }; var person33 = new Person3(); person33.sayName(); //hugo var person44 = new Person3(); person44.name = "hugo33"; person33.sayName(); //hugo person44.sayName(); //hugo33 console.log(person33.sayName == person44.sayName); //true //对原型中的初始值修改后,所有子实例都会修改初始值 Person3.prototype.name = "HUGO"; person33.sayName(); //HUGO person44.sayName(); //hugo33 //********************************************************* //用对象字面量的形式来创建原型,注意:此时是相当于创建了一个新的对象。此时constructor属性不再指向Person,而是指向Object,所以应该修改constructor的指向 function Person55() { }; Person55.prototype = { name: 'hugo', age: 18, sayName: function () { console.log(this.name) } } var person66 = new Person55(); console.log(Person55.constructor); //[Function: Function] console.log(person66.constructor); //[Function: Object] //正确写法如下 function Person66() { }; Person66.prototype = { constructor: Person66, name: 'hugo', age: 18, sayName: function () { console.log(this.name) } } var person77 = new Person66(); console.log(person77.constructor); //[Function: Person66] console.log(person77 instanceof Person66); //true //问题:对于一个实例的数组进行操作时,其他所有实例都会跟随着变化 function Animal() { } Animal.prototype = { constructor: Animal, name: "Dog", arr: [1,2] } var animal = new Animal(); var animal2 = new Animal(); console.log(animal.arr); //[ 1, 2 ] console.log(animal2.arr); //[ 1, 2 ] animal.arr.push(3); console.log(animal.arr); //[ 1, 2, 3 ] console.log(animal2.arr); //[ 1, 2, 3 ] console.log(animal.arr === animal2.arr); //true
组合使用构造函数模式和原型模式
是最常见的方式,构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性。支持向构造函数传递参数
优点:每个实例都有自己的一份实例属性的副本, 同时又共享着对方法的引用,最大限度节省内存。
function Student(name, age) { this.name = name; this.age = age; this.arr = [1,2]; } Student.prototype = { constructor: Student, sayName: function () { console.log(this.name) } } var student1 = new Student('jiang',18); var student2 = new Student('wei',18); student1.arr.push(3); console.log(student1.arr); //[ 1, 2, 3 ] console.log(student2.arr); //[ 1, 2 ] console.log(student1.arr === student2.arr); //false console.log(student1.sayName === student2.sayName); //true
动态原型模式
将所有信息都封装在构造函数中,通过在构造函数中初始化原型(必要情况下)由保持了同时使用构造函数和原型的优点
优点:可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型
注意点:在该模式下不能使用对象字面量重写原型。如果在已经创建了实例的情况下重写原型,会切断现有实例和新原型之间的联系
function Student(name, age) { this.name = name; this.age = age; // 只有在sayName()方法不存在的情况下,才会将它添加到原型中,if这段代码只会在初次调用构造函数时才会执行 // 这里对原型所做的修改,能够立即在所有实例中得到反映 if(typeof this.sayName != "function"){ Student.prototype.sayName = function () { console.log(this.name); } } } var student = new Student('hugo',18); student.sayName(); //hugo
寄生构造函数模式
在前几种模式不适用的情况下,可以使用寄生(parasitic)构造函数模式
创建一个函数,仅封装创建对象的代码,然后返回新创建的对象
与工厂模式的区别:使用new操作,并把使用的包装函数叫做构造函数
使用场景: 假设我们想创建一个具有额外方法的特殊数组,由于不能直接修改Array构造函数,就可以使用这个模式
注意点:返回的对象与构造函数或与构造函数的原型属性之间没有关系,也就是说,构造函数返回的对象与在构造函数外部创建的对象没有不同,不能依赖instanceof操作符来确定对象类型,如果可以使用其他模式的情况下,建议不要使用这种模式
function Student(name, age) { var o = new Object(); o.name = name; o.age = age; o.sayName = function () { console.log(this.name) } return o } var student = new Student('hugo', 18); student.sayName(); //使用场景 function NowArray() { var arr = new Array(); arr.push.apply(arr,arguments); arr.toNowString = function () { return this.join('-'); }; return arr } var nums = new NowArray('1','2','3'); console.log(nums); //[ '1', '2', '3', toNowString: [Function] ] console.log(nums.toNowString()); //1-2-3
稳妥构造函数模式
指的是没有公共属性而且其方法也不引用this的对象
使用场景:安全的环境中(这些环境会禁止使用this和new),防止数据被其他应用程序改动时使用
与寄生构造函数模式的区别:新建对象时不引用this,不适用new操作符构造函数。与寄生构造函数模式类似,该模式创建的对象与构造函数之间也没有什么关系,instanceof操作符无意义
function Student(name, age) { var o = new Object(); o.name = name; o.age = age; o.sayName = function () { console.log(this.name) } return o } var student = Student('hugo', 18); student.sayName();
闭包类
最关键的差别就是多了一层作用域
(function () { var a = 10; var People = function (name, age) { this.name = name; this.age = age; } People.prototype.hei = function () { console.log(a) } })()
单例模式
直接初始化,而是等到某一个条件触发的时候才初始化。而且,初始化的需求比较多的时候。
var Single = (function () { //定义一个构造函数 function People(name, age) { this.name = name; this.age = age; } //定义一个单例变量用于保存单例 var instance = null; //惰性单例 //非惰性 //var instance = new People("hugo", 18); console.log(instance); return function () { //惰性单例 if(instance === null){ return instance = new People("hugo", 18) }else{ return instance } //非惰性 //return instace } })()
安全类
在创建一个构造函数的实例的时候。如果我们知道它是一个构造函数,我们会自动的去使用new关键字 但是,如果不知道的人去书写我们的代码。那么就会出问题。
function People(name, age){ this.name = name; this.age = age; } 当不使用new的时候,我们执行一下 var people = People("hugo",18,); console.log(people); // 当People自己执行的时候,我们可以视为window.People执行。所以内部的this其实是window。 // 分析一下:People 与 new People在执行的时候,有什么区别? // 内部的this不同。 // 根据这个this来进行不同的处理。以便得到一致的结果
安全类就是为了解决这一类问题
function People(name, age){
// 如果this是People的实例 我们才可以使用this来赋值
if(this instanceof People){
this.name = name;
this.age = age;
}else{
// 不管什么方式调用,只要你的this不是People的实例,我就返回一个实例给你。
// People执行的时候,this不是People的实例,就是一个普通的函数在调用。那么一个普通函数是不会自动返回数据的。所以必须要加return关键字
return new People(name,age);
}
}