首页 > 编程语言 > 详细

JavaScript中的创建对象

时间:2021-01-18 14:36:56      阅读:18      评论:0      收藏:0      [点我收藏+]

本文主要内容

  • 什么是对象
  • 创建对象的几种方式

    • 使用构造函数创建
    • 字面量创建
    • 工厂模式
    • 构造模式
    • 原型模式

      • 原型
    • 组合使用构造函数模式和原型模式

什么是对象

直接上《JavaScript高级教程》的截图

技术分享图片

创建对象

1.使用构造函数创建

var obj = new Object();

2.字面量创建 

var obj = {};

3.工厂模式

如果使用构造函数和字面量创建很多对象,每个对象本身又有很多相同的属性和方法的话,就会产生大量重复代码,每个对象添加属性都需要重新写一次。如两个对象都需要添加name、age属性及showName方法:

var p1 = new Object();
p1.name = ‘张三‘
p1.age = ‘16‘,
p1.showName = function() {
    return this.name
}

var p2 = new Object();
p2.name = ‘李四‘
p2.age = ‘18‘,
p2.showName = function() {
    return this.name
}

为了解决这个问题,人们采用了工厂模式,抽象了创建对象的过程,采用函数封装以特定接口(相同的属性和方法)创建对象的过程。

function createPerson(name, age) {
    var obj = new Object();
    obj.name = name;
    obj.age = age;
    obj.showName = function () {
        return  this.name;
    };
    return obj;
}

var p1 = createPerson(‘张三‘, 16);
var p2 = createPerson(‘李四‘, 18);

4.构造模式

虽然工厂模式解决了创建多个对象的多个相同属性问题,却无法判定对象的具体类型,因为都是Object,无法识别是Array、或是Function等类型,这个时候构造函数模式出现了。

js中提供了像Object,Array,Function等这样的原生的构造函数,同时也可以创建自定义的构造函数,构造函数是一个函数,用来创建并初始化新创建的对象。将工厂模式的例子用构造函数可以重写为:

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

var p1 = new Person(‘张三‘, ‘16‘);
var p2 = new Person(‘李四‘, ‘18‘);

用Person代替了工厂模式的createPerson函数,而且函数名首字母P大写,这是因为按照惯例,构造函数首字母应该大写,而作为非构造函数的函数首字母小写。另外可以注意到构造函数内部的特点:

  1. 没有显示创建对象
  2. 直接在this上添加属性和方法
  3. 没有return

另外,还使用了new操作, 要创建一个实例,必须使用new操作符,使用new操作符调用构造函数,在调用构造函数的时候经历了如下几个阶段:

  1. 创建一个对象
  2. 把创建的对象赋值给this
  3. 执行函数中的代码, 即把属性和方法添加到赋值之后的this
  4. 返回新对象

伪代码来说明上述new Person()的过程如下:

// 使用new操作符时,会激活函数本身的内部属性[[Construct]],负责分配内存
Person.[[Construct]](initialParameters):

// 使用原生构造函数创建实例
var Obj = new NativeObject() //NativeObject为原生构造函数,如Object、Array、Function等

// 给创建的实例添加[[Class]]内部属性,字符串对象的一种表示, 如[Object Array]
// Object.prototype.toString.call(obj)返回值指向的就是[[Class]]这个内部属性
Obj.[[Class]] = Object/Array/Function;

// 给创建的实例添加[[Prototype]]内部属性,指向构造函数的prototype
O.[[Prototype]] = Person.prototype;

// 调用构造函数内部属性[Call],将Person执行上下文中this设置为内部创建的对象Obj,
this = Obj;
result = Person.[[Call]](initialParameters);  
// result是如果构造函数内部如果存在返回值的话,调用[[call]]时作为返回值,一般为Object类型

// 调用Person.[[call]]时,执行Person中的代码,给this对象添加属性和方法
this.name = name;
this.age = age;
this.showName = function() {
    console.log(this.name);
};

//如果Person.[[call]]的返回值result为Object类型
return result
// 否则
return Obj;

补充,贴出ECMAScript 5.1版本标准中[[Construct]]的规范:

技术分享图片

构造函数虽然解决了实例多个同名属性重复添加的问题,但是也存在每个实例的方法都需要重新创建一遍,因为每个方法都是Function的不同实例,看下面这段代码就明白了: 

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.showName = new Function("console.log(this.name);");
}

var p1 = new Person(‘张三‘, ‘16‘);
var p2 = new Person(‘李四‘, ‘18‘);
console.log(p1.showName === p2.showName); //false 

这个问题可以用以下办法来解决,把showName变成全局函数

但是这样如果对象需要添加很多方法就会产生很多全局函数,这些问题可以通过原型模式来解决

5.原型模式

什么是原型

当每一个函数创建时,都会给函数设置一个prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象包含所有实例共享的属性和方法,在默认情况下,都会为prototype对象添加一个constructor属性,指向该函数。

Person.prototype.constructor = Person;

原型模式就是不必在构造函数中定义实例的属性和方法,而是将属性和方法都添加到原型对象中。创建自定义构造函数,其原型对象只会默认取得constructor属性,其他的属性和方法都是从Object继承来的。当使用构造函数创建一个实例之后,会给实例添加内部属性[[prototype]],这个属性是一个指针,指向构造函数的prototype(原型)对象,由于是内部属性,无法通过脚本获取,但是在一些Chrome、Firefox、Safari等浏览器中在每个对象身上支持一个__proto__属性,指向的就是构造函数的原型对象。另外可以通过isProtoTypeOf()来判断创建的实例是否有指向某构造函数的指针,如果存在,返回true,如果不存在,返回false。

function Person() {

}
Person.prototype.name = ‘张三‘;
Person.prototype.friends = [‘张三‘, ‘李四‘];
Person.prototype.showName = function() {
    console.log(this.name);
}
var p1 = new Person();
var p2 = new Person()
console.log(p1.__proto__ === Person.prototype)  // true
console.log(Person.prototype.isPrototypeOf(p1))  // true

在ECMA5中增加了一个方法Object.getPrototypeOf(params),返回值就是创建对象的原型对象__proto__

console.log(Object.getPrototypeOf(p1) === Person.prototype); // true
console.log(Object.getPrototypeOf(p1).name);  //张三

原型模式虽然解决了方法共享的问题,但是对于实例共享来说是个比较大的问题,因为每个实例都需要有描述自己本身特性的专有属性,还是上面的代码:

console.log(p1.name)  // ‘张三‘
console.log(p2.name)  // ‘张三‘

另外对于属性是引用类型的值来说缺点就更明显了,如果执行下面这段代码:

p1.friends.push(‘王五‘);
console.log(p1.priends);  //[‘张三‘, ‘李四‘, ‘王五‘]
console.log(p2.priends);  //[‘张三‘, ‘李四‘, ‘王五‘]

为了解决原型模式的问题,人们采用了原型和构造组合模式,使用构造函数定义实例,使用原型模式共享方法。

6组合使用构造函数模式和原型模式

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.friends = [‘张三‘, ‘李四‘]; // this.friends = new Array(‘张三‘, ‘李四‘)
}

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

var p1 = new Person(‘John‘);
var p2 = new Person(‘Alice‘);
p1.friends.push(‘王五‘);
console.log(p1.friends); // [‘张三‘, ‘李四‘, ‘王五‘];
console.log(p2.friends); // [‘张三‘, ‘李四‘];
// 因为这时候每个实例创建的时候的friends属性的指针地址不同,所以操作p1的friends属性并不会对p2的friends属性有影响

console.log(p1.showName === p2.showName)  // true  都指向了Person.prototype中的showName

这种构造函数模式和原型模式组合使用,基本上可以说是js中面向对象开发的一种默认模式,介绍了以上这几种常用创建对象的方式, 还有其他不常用的模式就不介绍了,接下来想说的是js中比较重要的继承。

继承详情:https://www.cnblogs.com/vickylinj/p/14292158.html

JavaScript中的创建对象

原文:https://www.cnblogs.com/vickylinj/p/14292186.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!