Javascript深入理解对象

创建对象的几种方式:

1、字面量方式创建对象

var person={
    name:'张三',
    age:22,
    gender:'female'
}

缺点:如果要创建很多对象时,就要写很多这种代码块,较麻烦。

2、利用Object创建对象

var person = new Object();
person.name = '张三';
person.age = 22;
person.gender = 'female';

Object是js原生构造函数,可以直接在执行环境中使用。

缺点:即麻烦又没有括号包裹起来,不像一个整体。

3、工厂模式创建对象

function createPerson(name,age,gender) {
    var person = new Object();
    person.name = name;
    person.age = age;
    person.gender = gender;
    person.sayName = function(){
        console.log(this.name);
    }
    return person;
}
var person1=createPerson('fengjialu',22,'female');
var person2=createPerson('gaolingqiang',22,'male');

所谓工厂模式就是将创建对象的代码封装在一个函数中,作用就是降低代码冗余度,只要我们往工厂函数里塞参数,工厂函数就会像生产产品一样造个对象出来。

缺点:这种方式本质上是将创建对象的过程进行了封装。本质上并没有改变。例如我们创建一个student时无法知道其具体的数据类型,只知道这是一个对象,往往实际开发中我们需要确定这个对象到底是个Person的实例还是Dog的实例。

4、构造函数模式创建对象

function Person(name,age,gender) {
    //谁用这个构造函数创建对象实例,这个this就指向谁.
    this.name = name;
    this.age = age;
    this.gender = gender;
    this.sayName = function(){
        console.log(this.name);
    };
}
var person1 = new Person('gaolingqiang',22,'male');
var person2 = new Person('fengjialu',22,'female');

所谓构造函数和工厂函数的内部代码基本是一样的,只是有了以下区别:

1、没有显示地创建对象

2、属性和方法直接赋值给了this

3、没有返回值return

另外我们习惯将构造函数的名字首字母大写。

用构造函数创建对象就是加了个new操作符。构造函数也是函数,与普通函数唯一区别就是调用方式不同。任何函数只要使用new操作符调用就是构造函数,不使用就是普通函数。

构造函数创建对象的缺点:

每生成一个对象,函数内部的方法就会被重新复制一份,因此所有对象都有一个同名但不相等的函数,造成内存资源浪费。

因为都是做一样的事,所以没必要定义两个不同的 Function 实例。况且,this 对象可以把函数与对象的绑定推迟到运行时。

要解决这个问题,可以把函数定义转移到构造函数外部:

function Person(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.sayName = sayName;
}
function sayName() {
  console.log(this.name);
}

在这里,sayName()被定义在了构造函数外部。在构造函数内部,sayName 属性等

于全局 sayName()函数。因为这一次 sayName 属性中包含的只是一个指向外部函

数的指针,所以 person1 和 person2共享了定义在全局作用域上的 sayName()函

数。这样虽然解决了相同逻辑的函数重复定义的问题,但全局作用域也因此被搞乱

了,因为那个函数实际上只能在一个对象上调用。如果这个对象需要多个方法,那么

就要在全局作用域中定义多个函数。这会导致自定义类型引用的代码不能很好地聚集一起。

这个新问题可以通过原型模式来解决。

5、原型模式创建对象。

每一个函数都有自己的原型对象,原型对象的属性和方法可以被实例共享,原来在构造函数中直接赋给对象实例的值,可以直接赋值给它们的原型。

function Person(){}
Person.prototype.name = "zhangsan";
Person.prototype.age = 29;
Person.prototype.gender = "male";
Person.prototype.sayName = function () {
  console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName == person2.sayName); // true

这里,所有属性和 sayName()方法都直接添加到了 Person 的 prototype 属性上,构造函数体中什么也没有。但这样定义之后,调用构造函数创建的新对象仍然拥有相应的属性和方法。与构造函数模式不同,使用这种原型模式定义的属性和方法是由所有实例共享的。因此 person1 和 person2 访问的都是相同的属性和相同的sayName()函数。没有造成内存资源的浪费。

原型模式的问题:首先,它弱化了向构造函数传递初始化参数的能力,会导致所有实例默认都取得相同的属性值。虽然这会带来不便,但还不是原型的最大问题。原型的最主要问题源自它的共享特性。我们知道,原型上的所有属性是在实例间共享的,这对函数来说比较合适。另外包含原始值的属性也还好,因为可以通过在实例上添加同名属性来简单地遮蔽原型上的属性。真正的问题来自包含引用值的属性。来看下面的案例:

function Person() { }
Person.prototype = {
  constructor: Person,
  name: "zhangsan",
  friends: ["lisi", "wangwu"],
  sayName() {
    console.log(this.name);
  }
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("zhaoliu");
console.log(person1.friends); // [ 'lisi', 'wangwu', 'zhaoliu' ]
console.log(person2.friends); // [ 'lisi', 'wangwu', 'zhaoliu' ]
console.log(person1.friends === person2.friends); // true

我们给person1的数组添加了一个赵六,但是person2的数组也会添加一个赵六,但是我们并不想这样,原因是引用类型共享的时候是共享的内存地址,因此所有实例共享的引用类型的数据都是一样的。因此这就是原型模式的缺点,因此我们需要用组合模式来完善原型模式和构造函数模式,取长补短,这也是开发中使用最广泛,认同度最高的一种创建自定义类型的方法。

function Person(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.firends = ['zhangsan', 'lisi'];
}
Person.prototype = {
  constructor: Person,
  sayName: function () {
    console.log(this.name);
  }
};
var p1 = new Person('larry', 44, 'male');
var p2 = new Person('terry', 39, 'male');

p1.firends.push('robin');
console.log(p1.firends); // [ 'zhangsan', 'lisi', 'robin' ]
console.log(p2.firends); // [ 'zhangsan', 'lisi' ]
console.log(p1.firends === p2.firends); // false
console.log(p1.sayName === p2.sayName); // true

总结就是:构造函数用于定义实例属性,原型模式用于定义方法和共享属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值