JS学习笔记 - Object

理解对象

属性的类型

  1. 数据属性

    数据属性包含数据值

    特性描述
    [[Enumerable]]属性是否可枚举
    [[Configurable]]属性是否可以通过delete删除并重新定义,是否可以修改它的特性
    [[Writable]]属性是否可以被修改
    [[Value]]属性实际的值
  2. 访问器属性

    访问器属性不包含数据值,而包含一个getter和setter函数

    特性描述
    [[Enumerable]]属性是否可枚举
    [[Configurable]]属性是否可以通过delete删除并重新定义,是否可以修改它的特性
    [[Get]]属性的获取函数
    [[Set]]属性的设置函数

自定义属性

Object.defineProperty(object,string,object)

  • object - 要操作的对象
  • string - 要操作的属性名
  • object - 属性描述对象

Object.defineProperties(object,{'prop1':{},'prop2':{},...})

  • object - 要操作的对象
  • object - 要传入的多个参数

获取属性的特性

使用Object.getOwnPropertyDescriptor(object,prop)方法可以获取指定属性的属性描述符

合并属性

ES6提供了Object.assign(target,obj1,obj2,...)方法,这个方法将每个原对象中可枚举和自有(Object.hasOwnProperty()返回true)的属性复制到目标对象。如果多个对象都有相同的属性,则使用最后一个复制的值

Object.assign()方法进行的是浅复制

属性的枚举

返回可枚举的属性

  • for…in循环
  • Object.keys() //返回所有可枚举属性名

返回所有属性

  • Object.getOwnPropertyNames()获取所有属性名称
  • Object.getOwnPeopertySymbols()
  • in操作符

创建对象

对象字面量

var obj = {}
var o = new Object();

缺点:创建过多重复代码,占用内存空间,代码过于冗杂

工厂模式

function Dog(){
  var o = new Object();
  o.name = 'hello';
  return o;
}
var o = Dog();

缺点:无法解决对象标识问题,所有实例都是Object类型

构造函数模式

function Dog(name){
  this.name = name;
  this.getName = funciton(){
    return this.name;
  }
}
var o = new Dog('hello')

缺点:每个实例都重复声明代码,占用内存空间

new操作符

以new操作符新建对象时调用构造函数会执行如下操作:

  1. 在内存中创建一个新对象
  2. 将新对象内部的[[prototype]]特性赋值为构造函数的prototype属性
  3. 将构造函数内部的this绑定给这个对象
  4. 执行构造函数内部的代码
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象

拓展构造函数模式

function getName(){
  return this.name;
}
function Dog(name){
  this.name = name;
  this.getName = getName;
}
var o = Dog('hello')

缺点:污染了全局变量空间。

寄生构造函数模式

function Dog(name){
  var o = new Object();
  o.name = name;
  return o;
}
var o = new Dog('hello');

缺点:无法使用instanceof和prototype,占用内存空间

稳妥构造函数模式

function Dog(name){
  var o = new Object();
  o.getName = function(){
    return name;
  }
  return o;
}
var o = Dog('hello');

缺点:无法使用instanceof和prototype

原型模式

function Dog(){}
Dog.prototype.name = 'hwllo';

缺点:所有实例共享属性;

Object.setPrototypeOf()方法,可以将实例的私有特性[[Prototype]]写入一个新值。这样就可以重写一个对象的原型继承关系

为了避免Object.setPrototypeOf()方法可能造成性能下降,可以使用Object.create()来创建新对象,同时为其执行原型

其他原型语法

为了减少冗余,直接通过一个包含所有属性和方法的对象字面量来重写原型对象

function Person() {} 
Person.prototype = {
    name: "Nicholas", 
    age: 29, 
    job: "Software Engineer", 
    sayName() { 
        console.log(this.name); 
    } 
};

在这个例子中,Person.prototype 被设置为等于一个通过对象字面量创建的新对象。最终结果是一样的,只有一个问题:这样重写之后,Person.prototype 的 constructor 属性就不指向 Person了。在创建函数时,也会创建它的 prototype 对象,同时会自动给这个原型的 constructor 属性赋值。而上面的写法完全重写了默认的 prototype 对象,因此其 constructor 属性也指向了完全不同的新对象(Object 构造函数),不再指向原来的构造函数。虽然 instanceof 操作符还能可靠地返回值,但我们不能再依靠 constructor 属性来识别类型了。如果 constructor 的值很重要,则可以像下面这样在重写原型对象时专门设置一下它的值:

function Person() { } 

Person.prototype = { 
    constructor: Person,
    name: "Nicholas", 
    age: 29, 
    job: "Software Engineer", 
    sayName() { 
        console.log(this.name); 
    } 
}; 

这次的代码中特意包含了 constructor 属性,并将它设置为 Person,保证了这个属性仍然包含恰当的值。但这种方式恢复constructor属性会创建一个[[Enumerable]]为true的属性,而原生的constructor属性是不可枚举的。为了解决这个问题,可以使用Object.defineProperty()方法来定义constructor属性

Object.defineProperty(Person.prototype, "constructor", { 
    enumerable: false, 
    value:function(){
        //代码
    } 
});

组合模式

function Dog(name){
  this.name = name;
}
Dog.prototype.getName = function(){
  return this.name;
}

动态原型模式

function Dog(name){
  this.name = name;
  if(typeof this.getName != 'function'){
    Dog.prototype.getName = function(){
      return this.name;
    }
  }
}

继承

原型链继承

function Animal(){}
function Dog(){}
Dog.prototype = new Animal()

借用构造函数继承

function Animal(){}
function Dog(){
  Animal.call(this);
}

组合继承

function Animal(){}
function Dog(){
  Animal.call(this);
}
Dog.prototype = new Animal();

寄生式继承

寄生式继承背后的思路类似于寄生构造函数和工厂模式:创建一个实现继承的函数,以某种

方式增强对象,然后返回这个对象

function createAnother(original){ 
    let clone = object(original); // 通过调用函数创建一个新对象
    clone.sayHi = function() { // 以某种方式增强这个对象
        console.log("hi"); 
    }; 
    return clone; // 返回这个对象
}

寄生组合式继承

组合继承其实也存在效率问题。最主要的效率问题就是父类构造函数始终会被调用两次:一次在是创建子类原型时调用,另一次是在子类构造函数中调用。

function inheritPrototype(subType, superType) { 
    let prototype = object(superType.prototype); // 创建对象
    prototype.constructor = subType; // 增强对象 
    subType.prototype = prototype; // 赋值对象
}

Object的静态方法

Object.keys() //获取对象的所有属性
Object.getPropertyNames() //获取对象的所有属性
Object.getPrototypeOf() //获取对象的原型对象
Object.setPrototypeOf() //设置对象的原型对象
Object.create() //将参数传入的对象构建一个对象传出

Object的实例方法

Object.prototype.valueOf()
Object.prototype.toString()
Object.prototype.toLocaleString()
Object.prototoye.hasOwnProperty() //对象本身是否具有该属性(不包括继承)
Object.prototype.isPrototypeOf() //对象是否是某个对象的原型对象
Object.prototype.propertyIsEnumerable() //属性是否为可枚举属性
Object.prototype.getOwnPropertyDescriptor() //获取参数的属性描述对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值