JS学习笔记 - Object
理解对象
属性的类型
-
数据属性
数据属性包含数据值
特性 描述 [[Enumerable]] 属性是否可枚举 [[Configurable]] 属性是否可以通过delete删除并重新定义,是否可以修改它的特性 [[Writable]] 属性是否可以被修改 [[Value]] 属性实际的值 -
访问器属性
访问器属性不包含数据值,而包含一个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操作符新建对象时调用构造函数会执行如下操作:
- 在内存中创建一个新对象
- 将新对象内部的[[prototype]]特性赋值为构造函数的prototype属性
- 将构造函数内部的this绑定给这个对象
- 执行构造函数内部的代码
- 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象
拓展构造函数模式
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() //获取参数的属性描述对象