JS创建对象

JS中创建对象的7种方式

讲原型链之前需要了解的,JS中创建对象的方式(这里的创建对象是指创建多个相似对象,它们有共同的属性和方法),高程上介绍了7种:工厂模式、构造函数模式、原型模式、组合使用构造函数和原型模式、动态原型模式、寄生构造函数模式、稳妥构造函数模式。

这里主要讲工厂模式-->构造函数模式-->组合使用构造函数和原型模式三种的层层递进。

1. 工厂模式

function createPerson(name){
	var obj = {};
	obj.name = name;
	obj.sayHello = function(){
		console.log(this.name);
	};
	return obj;
}
var p = createPerson("Jack"); 
  • 特点:工厂模式执行函数创建对象,不需要new关键字,在函数内部,自己创建对象,返回对象。
  • 优点:工厂模式像一个工厂,每次加工产生一个对象,解决了创建多个相似对象的问题。
  • 缺点:没有解决对象识别的问题,即,怎样知道一个对象的类型(instanceof运算符)。

2. 构造函数模式

function Person(name){
    this.name = name;
    this.fn = function () {
        console.log(this.name);
    }
}
var p1 = new Person("Nicholas");
var p2 = new Person("Lucy");
console.log(p1.fn === p2.fn);	// =>false

上面的代码就是一个构造函数。构造函数的两面性:

  1. 构造函数是一个类。构造函数执行的时候,通过new 函数名( )来创建实例对象,此时构造函数称之为一个类,而p就是当前类的一个实例。
    注:类都是函数数据类型的,类的实例都是对象数据类型的。
  2. 类也是一个普通函数。所以也会像普通函数执行一样,形成一个私有作用域,有形参赋值、变量提升,然后代码从上到下执行。但是构造函数在代码自上而下执行之前,浏览器默认首先会创建一个对象数据类型的变量(相当于工厂模式里面手动创建的var obj = {};这里不需要了) ,并且会默认返回它(即return obj;也不需要了)。把浏览器默认创建的这个对象当做当前函数执行的整体(this),然后通过this.属性名 = 值;给默认创建的对象增加属性和方法。
    注:浏览器默认创建的这个对象其实就是当前类的实例。

举个栗子:

function Person(name){
    var num = 13;
    this.name = name;
    this.fn = function () {
        console.log(this.name);
    }
}
var p1 = new Person("杨");

执行上面这段代码的时候,浏览器会给Person创建一个私有作用域,然后Person中的代码自上而下执行:框图
注意3点:

  1. 在类中通过this.XXX = XXX;给实例增加的属性和方法都是当前实例的私有属性和私有方法。
  2. 类中出现的私有变量(var num = 13;)是把它看作普通函数执行的时候产生的,和这个类的实例对象p1没有任何关系,p1并没有num这个属性,只有通过this.XXX = XXX;添加的才是实例的私有属性。
  3. 构造函数不需要写return(返回值),浏览器也会把创建的实例进行返回(默认返回this),但是如果构造函数内部被手动添加了返回值呢?这里分两种情况:
    function Person(name) {
        this.name = name;
        //...
        //reutrn 12;
        return {};
    }
    var p = new Person("AAA");
    
    (1) return 12;自己手动返回一个基本数据类型的值,对创建的实例没有影响。
    (2) return {};自己手动返回一个引用数据类型的值,会把浏览器默认的return this;覆盖,此时p不是Person的实例了,p = {}
    function Person(name) {
       this.name = name;
       //...
       return {};
    }
    var p = new Person("AAA");
    console.log(p instanceof Person);   // =>false
    

构造函数的优点:解决了工厂模式中的问题,可以识别对象所属的类。

function Person(name) {
    this.name = name;
    //...
}
var p = new Person("AAA");
console.log(p instanceof Person);   // =>true

构造函数的缺点:基于构造函数创建的实例对象,所有的属性和方法都是私有的,没有共有方法。

3. 组合模式:构造函数+原型模式

原型模式创建的对象,所有的属性都是公有的。

//原型模式创建对象

function Person() {

}
Person.prototype.name = "Nicholas";
Person.prototype.age = 12;
Person.sayName = function () {
    console.log(this.name);
};

var p1 = new Person;
var p2 = new Person;
console.log(p1.sayName === p2.sayName); //=>true

组合使用构造函数模式和原型模式(既有公有方法又有私有方法):

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.sayOld = function () {

    };
}
Person.say = function () {
    console.log(this.name);
};

var p1 = new Person("AAA", 12);
var p2 = new Person("BBB", 13);
console.log(p1.sayOld === p2.sayOld); //=>false
console.log(p1.say === p2.say); //=>true

注意三点:

  1. 所有的函数数据类型(普通函数、类)都天生自带一个属性:prototype(原型),并且这个属性的值是一个对象数据类型,浏览器默认为这个对象数据类型开辟一个堆内存xxxfff-pro
  2. 由于xxxfff-pro是浏览器默认开辟的堆内存,在这个堆内存中,天生自带一个属性:constructor(只有浏览器默认给prototype开辟的堆内存中才有这个属性),存储的是当前类本身。
    Person.prototype.constructor === Person; //=>true
    
  3. 所有的对象数据类型(普通对象{}、类的实例、prototype)都天生自带一个属性__proto__,这个属性指向当前实例(对象)所属类的原型(prototype)。
    Person.prototype.__proto__.constructor === Object; //=>true
    

总结就是:
① 所有的函数都自带prototype
② 所有的对象都自带__proto__
在这里插入图片描述
原型链机制:通过new Person创建当前Person类的一个实例p,则p就拥有了这个类中所有的属性和方法(私有+公有),对于p.say(),按照如下机制来查找:

  1. 先在当前实例上找私有的,私有的存在,则获取的就是私有的(不管公有是否存在),若私有的没有,则通过__proto__找公有的。
    p.say === p.__proto__.say; //=>true
    
  2. 若公有上也不存在,则通过prototype上的__proto__继续向上找,一直找到Object.prototype为止。
    p.hasOwnProperty === p.__proto__.__proto__.hasOwnProperty; //=>true
    //hasOwnProperty存在于Object.prototype中。
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值