JavaScript学习之对象

一、理解对象

ECMAScript 中有两种属性:数据属性和访问器属性
1、数据属性
数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性:

  • [[Configurable]]: (可配置)
    表示能够通过delete删除属性,从而重新定义属性,表示能够修改属性的特性,或能否把属性修改 为访问器属性。
    即用来表示当前属性是否能够被更改。
    默认值: true
  • [[Enumerable]]: (可枚举)
    表示能否通过for-in循环返回属性。
    默认值: true
  • [[Writable]]: (可写)
    表示能否修改属性的值。
    默认值:true
  • [[Value]]:
    包含这个属性的数据值。读取属性时,从该位置读;写入属性时,把新值保存在这个位置。
    默认值:undefined

使用Object.defineProperty()可修改属性默认特性。
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性。
Object.defineProperty(obj, prop, descriptor)
参数:
obj - 要定义属性的对象(属性所在对象)
prop - 要定义或修改的属性的名称或Symbol() (属性的名字)
descriptor - 要定义或修改的属性描述符(行为特征描述配置)

注:

  • 应当直接在 Object 构造器对象上调用此方法,而不是在任意一个 Object 类型的实例上调用。
  • 调用Object.defineProperty()方法时,如果不指定configurable/enumerable/writable,
    则他们都会被设置为false
  • 当configurable设置为false的过程是不可逆的,即不可再将该属性设置为true。
    此时,再调用 Object.defineProperty()方法修改除 writable 之外的特性,都会导致错误。

2、访问器属性
访问器属性不包含数据值,他们包含一对儿getter和setter函数。
进行读操作时,会访问getter函数;进行写操作时,会访问setter函数。
getter负责返回值,setter负责处理数据。

二、创建对象

1、工厂模式(可用于创建多个类似对象,但无法识别对象类型)

 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;
    }
    const person1 = createPerson('Wegin', 20, 'Soft Engineer');
    const person2 = createPerson('Niki', 28, 'Soft Engineer');

2、构造函数模式

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

const person1 = new Person('Wegin', 20, 'Soft Engineer');
const person2 = new Person('Niki', 28, 'Soft Engineer');

使用构造函数创建实例需使用new操作符,new操作符使用过程中进行了以下步骤:
(1)创建一个新对象
(2)将构造函数的作用域赋值给新对象(因此this指向了新对象)
(3)执行构造函数中的代码(为这个新对象添加属性)
(4)返回新对象
使用构造函数的主要问题,就是每个方法都要在实例上创建一遍。
使用构造函数创建的对象,意味着可以将该实例标识为一种特定的类型。
在该例子中,person1和person2都是Object的实例,因为所有对象均继承自Object.

person1.__proto__ === Person.prototype; // true
person1.__proto__.__proto__ === Object.prototype; // true
person1 instanceof Person; // true
person1 instanceof Object; // true
Person instanceof Object; // true

3、原型模式
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

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

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

原型链查找过程描述:
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具
有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找
到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继
续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属
性。如果在原型对象中找到了这个属性,则返回该属性的值。

原型与in: 有两种方式使用in操作符,单独使用和在for-in循环中使用。
在单独使用时,in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。
hasOwnProperty()只在属性存在于实例中时才返回true

因此,判断属性是否是原型中的,可以组合in和hasOwnProperty()来判断。
如果in 返回true, hasOwnProperty()返回false,则该属性在原型中。

原生对象的原型
原型模式的重要性不仅体现在创建自定义类型方面,就连所有原生的引用类型,都是采用这种模式创建的。
所有原生引用类型都在其构造函数的原型上定义了方法。

原型模式的问题:引用类型共享,如果有一个实例对引用类型数据进行更改,则所有实例上的该属性都将被更改。

4、组合使用构造函数模式和原型模式
创建自定义类型的最常见方式,就是组合使用构造函数和原型模式。
构造函数用于定义实例属性,而原型模式用于定义方法和共享的属性。结果,每个实例都会有自己的一份实例副本,同时又共享着对方法的引用。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ['Jane', 'Lily'];
}

Person.prototype = {
    // 改写原型对象后,constructor指向也被改变了,因此需要手动设置
    constructor: Person,
    sayName: function () {
        console.log(this.name);
    }
}

const person1 = new Person("Jane", 29, "Software 
Engineer");
const person2 = new Person("Lily", 27, "Doctor"); 

5、动态原型模式
由于在原型中查找值得过程是一次搜索,因此我们对原型对象所做的任何修改都能够立即从实例上反映出来 —— 即使是先创建了实例后修改原型也照样如此。

var friend = new Person();
Person.prototype.sayHi = function() {
    console.log('hi');
}
friend.sayHi(); // 'hi'

尽管可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但
如果是重写整个原型对象,那么情况就不一样了。
如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系。

在调用构造函数的时候,会为实例添加一个指向最初原型的__proto__指针,而把原型修改为另外一个对象,就等于切断了构造函数与最初原型之间的联系。(实例中的指针仅指向原型,而不指向构造函数)

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    if (typeof this.sayName !== 'function') {
        Person.prototype.sayHi = function() {
            console.log('hi');
        }
    }
}

6、寄生的构造函数模式

function Person(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;
}
const friend = new Person("Lily", 29, "Software Engineer");
friend.sayName(); //"Lily" 

构造函数在不返回值得情况下,会默认返回新对象实例。而如果给构造函数添加返回值得话,
则可以重写调用构造函数时返回的值。(添加的返回值应是Object的实例,否则还是会返回新的对象实例)

7、稳妥构造函数模式

function Person(name, age, job){
    //创建要返回的对象
    var o = new Object();
    //可以在这里定义私有变量和函数
    //添加方法
    o.sayName = function(){
        alert(name);
    };

    //返回对象
    return o;
} 

稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用 this 和 new),或者在防止数据被其他应用程序(如 Mashup程序)改动时使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值