对象的创建方式-简单整理

1、工厂模式
用函数封装,以特定的接口创建对象。
如下所示:显示的创建一个Object对象,然后返回该对象即可;
1-1、具体实现
function createPerson (name) {
    var obj = new Object();// 实例话一个Object对象
    obj.name = name;
    obj.getName = () => {
        console.log(this.name);
    };
    return obj;
}
var p1 = createPerson('zhangsan');
var p2 = createPerson('lisi');
1-2、优缺点
优点:可以无数次的调用这个函数,创建多个不同的对象;
缺点:无法确定一个对象的具体类型,即属于那个函数;
2、构造函数模式
2-1、具体实现
function Person (name) {
    this.name = name;
    this.getName = () => {
        console.log(this.name);
    }
}
var p1 = new Person ('zhangsan');
var p2 = new Person ('lisi');
2-2、与工厂模式的不同
1>、没有显示的创建Object对象;
2>、将属性赋给来this对象;
3>、没有return语句;
4>、函数的首字母大写
5>、创建对象时,使用来【new】关键字
2-3、new运算符

2-3-1、作用

创建一个用户定义的对象类型的实例,或者具有构造函数的内置对象的实例。

2-3-2、执行new Person(…)时,发生的事情

1>、一个继承自Person.prototype的新对象被创建;
2>、使用指定的参数调用构造函数Person,并将this对象绑定到新创建的实例对象上。
3>、由构造函数返回的对象就是new表达式的结果。如果构造函数没有显示的返回一个对象,则使用步骤1创建的对象。

2-3-3、使用new操作符创建对象实例的步骤

1>、创建一个新对象;
2>、将构造函数的作用域赋给新对象;
3>、执行构造函数中的代码;
4>、返回新对象

2-3-4、其他

1>、每创建一个实例对象,该对象就会有一个constructor属性,指向Person;
2>、所有的对象都继承自Object对象,所以实例对象也是Object对象的实例;

2-3-5、问题

使用这种方式实例话对象时,每个方法都要在实例上重新创建一遍,会创建多个重复的方法,浪费内存。
#解决方法
定义全局的函数,将实例对象的属性指向这个函数的引用,避免了创建多个相同的function。
function Person (name) {
    this.name = name;
    this.getName = getName;
}
function getName () {
    console.log(this.name);
}
3、原型模式
3-1、具体实现
function Person () {}

Person.prototype.name = 'zhangsan';
Person.prototype.getName = function () {
    console.log(this.name);
}
// p1与p2的对象的name属性的值是一样的
var p1 = new Person ();
var p2 = new Person ();
3-2、原型对象prototype
1>、每一个函数都有一个prototype属性,这个属性是一个指针,指向原型对象。
2>、用途:包含可以由特定类型的所有实例共享的属性和方法。
3>、原型对象有一个constructor属性,即:Person.prototype.constructor,constructor指向函数体,即Person函数。
3-3、与构造函数的不同
该对象的所有属性和方法是所有实例共享的,所以上述的p1和p2共享同一个name属性
3-4、实例对象、构造函数和原型之间的关系
1>、function Person(){}是一个函数对象,它本身有一个prototype属性,该属性是一个指针,指向Person对象的原型对象;
2>、Person.prototype对象有一个constructor属性,也是一个指针,指向Person函数体;
3>、var p = new Person();创建了一个对象实例,该实例有一个__proto__属性,该属性指向Person的原型对象【Person.prototype】;
4>、可以得出:实例对象与构造函数之间没有直接的关系,他们是通过Person的原型对象【Person.prototype】建立的联系;
4>、每一个构造函数都是new Function()的实例,所以Person.__proto__指向Function的prototype;
5>、原型对象最初只包含constructor属性,而该属性也是共享的,所以可以通过对象实例访问,即p.constructor;
6>、实例中的指针仅指向原型,不指向构造函数。
3-5、原型中常用的方法
1>、isPrototypeOf():判断实例与构造函数是否存在某种关联;
    用法:Person.prototype.isPrototypeOf(p);
2>、getPrototypeOf():获取指定实例对象的原型对象;
    用法:Object.getPrototypeOf(p);
3>、hasOwnProperty():检测一个属性是存在于实例中,还是存在于原型中;【该方法继承自Object】
4>、in操作符:只要属性能够被访问到,就返回true,无论是在实例对象上被访问,还是在原型对象上被访问;
5>、Object.getOwnPropertyNames():获取所有实例属性,包括不可枚举的constructor属性;
6>、Object.keys():获取对象上的所有可枚举属性;返回一个包含所有可枚举属性的字符串数组
3-6、原型中属性的访问
可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值;
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;即:添加这个属性只会阻止我们访问原型对象中的属性,但是不会修改那个属性;
即使将实例对象上的属性设置为null,也不会修改原型对象上的属性;除非使用delete操作符删除这个属性。
3-7、问题
在原型对象上添加属性和方法时,只能一个一个的添加;
#解决方法:使用字面量语法重写原型对象
重写原型对象,会导致构造函数的constructor的指向发生变化,需要重新指明constructor的指向。
重设constructor属性会导致它的enumerable被设置为true,而原生的constructor属性是不可枚举的。
function Person () {}
Person.prototype = {
    constructor: Person,
    name: 'zhangsan',
    getName: function () {
        console.log(this.name);
    }
}
var p = new Person ();
3-8、重写原型对象
重写原型对象,对象实例化必须在重写原型对象之后;
重写原型对象断开了实例与原型对象之间的关系;
#错误的实现
function Person () {}
var p = new Person ();
Person.prototype = {
    constructor: Person,
    name: 'zhangsan',
    getName: function () {
        console.log(this.name);
    }
}
p.getName();// 调用函数的时候会报错
4、构造函数模式与原型模式的组合
代码实现:
function Person (name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype = {
    constructor: Person,
    getName: function () {
        console.log(this.name);
    }
}
var p = new Person ('zhangsan', 24);// 属性不共享,方法共享
5、动态原型模式
代码实现
判断语句只有在初始化构造函数时才会执行;
在动态原型模式中,不能使用对象字面量的方式重写原型;
只需使用if语句检查其中的一个属性或方法即可。
function Person (name, age) {
    this.name = name;
    this.age = age;
    
    if(typeof this.sayName != 'function') {
        Person.prototype.sayName = function () {
            console.log(this.name);
        }
    }
}
var p = new Person ('zhangsan', 24);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章