学习笔记——对象方法整理

2,223 阅读12分钟

在前面写过两篇文章,分别是关于数组和字符串的方法解析,那这三剑客之一的对象怎么能不写呢,本次来整理一下对象有哪些常用的方法,下面介绍的每个方法都会写与示例,方便大家对照查看。^_^

对象是指: 成对的名称(字符串)与值(任何值),其中名称通过冒号与值分隔;

一、创建对象的方式,以下列出了四种,可根据实际情况来选择

(1)字面量形式(最常用),在实际中运用较多,较方便,易上手:

let object={
    a:1,
    b:2
};
console.log(object);       //{a:1,b:2}

(2)对象实例(传统的形式,在操作对象时用的较频繁。):

let object=new Object();
    object.a=1;
    object.b=2;
console.log(object);        //{a:1,b:2}

(3)构造函数形式:

function employee(name,job,born) {
    this.name=name;
    this.job=job;
    this.born=born;
}
let bill=new employee("Bill Gates","Engineer",1985);
 
// employee {name: "Bill Gates", job: "Engineer", born: 1985} 

(4)工厂模式:

工厂模式的优势是能够一次性出厂多个相同的属性的对象,节省资源占用,缺点是:属性都为相同的,如果要创建不同的需要重新生成。

function create(name,job,born){
    let people=new Object();
    people.name=name;
    people.job=job;
    people.born=born;
    people.show=function(){
    console.log("name:"+this.name+","+"job:"+this.job+","+"born:"+this.born)
    };
    return people;
}
let friend=create("Christine","teacher","1990");
 
friend.show();  //name:Christine,job:teacher,born:1990

(5)原型模式

将属性赋值到对象的原型上,并不会影响其本身的属性。

function create(name,job,born){};
    create.prototype.name="Christine";
    create.prototype.job="teacher";
    create.prototype.born="1990";
    create.prototype.show=function(){
    console.log("name:"+this.name+","+"job:"+this.job+","+"born:"+this.born)
};
let friend=new create();
friend.show();   //name:Christine,job:teacher,born:1990

(6)ES6 class模式

  //定义类
    class Point {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            };
            //定义方法
            toString() {
                return '(' + this.x + ', ' + this.y + ')';
            }
            multiplication() {
                return this.x * this.y
            }
            test() {
                return '1231354654'
            }
        }
        console.log(Point.name)  
        //Point (name属性总是返回紧跟在class关键字后面的类名。)
        
        // 实例化并调用(实例化和调用方法同es5相同)
        let x = new Point(5, 6)
        console.log(x.toString(), '相乘结果:' + x.multiplication(), x.test())
        // (5, 6) 相乘结果:30 1231354654
       
        // 快速添加方法到原型上 (如下图,直接可添加到原型)
        Object.assign(Point.prototype, {
            dosomething() { },
            toValue() { }
        });
        console.log(Point.prototype)

二、Object.assign()浅拷贝 && JSON.parse(JSON.stringify())深拷贝

拷贝:其实就是一个对象复制给另外一整个对象,让对象相互不影响。对象的拷贝又分为浅拷贝和深拷贝:对象的浅拷贝(只拷贝一层);对象的深拷贝(拷贝多层);

!注意:浅拷贝和深拷贝只针对object和Array这样的复杂的对象

Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象。 Object.assign(target, ...sources)

参数:

target:目标对象;sources:源对象;返回值:目标对象。

例:

(1)用来复制一个新对象,并不会影响原对象

let obj = { a: 1 };
let copy = Object.assign({}, obj);
console.log(copy);    // { a: 1 }

(2)用来合并对象属性,将源对象的所有可枚举属性,复制到目标对象。

//object.assign(obj, obj2)  obj2是源对象,obj 是目标对象,返回目标对象
 
let obj = { a: 1 };
let obj2={b:2};

console.log(Object.assign(obj,obj2)===obj);  //true,返回目标对象
console.log(obj);       //{a:1,b:2} obj的值已被更改

(3)如果目标对象和源对象中有相同的键,则属性将被源对象的属性覆盖,后面的源属性会覆盖之前的相同键的源属性。

let obj = { a: 1 };
let obj2 = {a:5,b:2};
let obj3 = {b:1,d:0};
Object.assign(obj,obj2,obj3);

console.log(obj);       // {a: 5, b: 1, d: 0}

obj和obj2同时拥有相同的键a,但两个值不同,obj是目标对象,所以会被源对象obj2的值覆盖,obj2obj3也同时拥有相同的键b,在拷贝时,obj3排在obj2的后面,所以obj2被覆盖 ,最终打印结果是:{a:5,b:1,d:0}

(4)当assign只有一个对象时,则直接返回这个对象,不做任何操作;

let obj = { a: 1 }
Object.assign(obj);
console.log(obj);        //{a:1}

(5)Object.assign()方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
obj1.b.c=5;

console.log(obj2)       //{a:0,b:{c:5}};

当我们在改变obj1的值时,并没有想改变obj2,但obj2的值也发生了改变,这违背了我们的想法。

(6)给当前的对象添加新的属性

对象尽量静态化,一旦定义,就不得随意添加新的属性。如果添加属性不可避免,要使用Object.assign方法。

const a = {};
Object.assign(a, { x: 3 });  // a => x:3

如果添加相同的属性,旧属性的值会被新的值覆盖:

const a = { x: 5 };
Object.assign(a, { x: 3 });  // a => x:3

(7)JSON.parse(JSON.stringify())实现深拷贝

let obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(obj3);     //{ a: 0 , b: { c: 0}};

上述方法,大部分情况是可以解决,但有以下几种情况无法识别:

  1. 会忽略 undefined
  2. 会忽略 symbol
  3. 不能序列化函数
  4. 不能解决循环引用的对象

针对这几个问题,也不是没有解决方法,可以自己去实现一个深拷贝,但需要考虑各种类型以及边界值,我更推荐使用 lodash工具库的深拷贝函数cloneDeep()

三、Object.entries()

返回一个数组,其元素是与直接在object上找到的可枚举属性键值对相对应的数组。属性的顺序与通过手动循环对象的属性值所给出的顺序相同。

例: (1).当键值为正常字符时,返回的键的值为字符串

let obj1 = {a:1,b:2};
let obj2 = Object.entries(obj1);
console.log(obj2) ;       //  [ ["a":1],["b":2] ] 

(2)当键值为数字时,返回的键的值也是字符串

let obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj));     // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ] 

(3)当键值的数字错乱无序时,它会将其排序后返回

let anObj = { 100: 'a', 2: 'b', 9: 'c' };
console.log(Object.entries(anObj));   //[["2","b"],["9","c"],["100","a"]]

(4)展开对象

const object1 = {
 a: 'somestring',
 b: 42
};

for (let [key, value] of Object.entries(object1)) {
 console.log(`${key}: ${value}`);
}
//     "a: somestring"
//     "b: 42"

Object.fromEntries()Object.entries() 是相对的

用于将一个键值对的列表转为对象

Object.fromEntries([ ['foo', 1], ['bar', 2] ]) // {foo: 1, bar: 2}

四、Object.is()方法判断两个值是否是相等的值

Object.is(value1, value2);

参数:

value1:需要比较的第一个值。 value2:需要比较的第二个值。 返回值:表示两个参数是否相同的Boolean

(1)比较字符串

比较字符串(单纯的比较字符串的每一位,如果完全相同返回true,只要有一个不一样,返回false)

Object.is('foo', 'foo’);          //true;  

Object.is('fo', 'foo’);           //false

(2)比较数组

比较数组 (对数组无感,即使是相同的数组,或者是相同的空数组,都返回false)

Object.is([11,12],[11,12]).        //false
 
Object.is([],[])                   //false 

(3)比较window

Object.is(window, window)           //true
 
Object.is(window, document);       //false

(4)比较特殊对象undifined 和null

undefinednull都等于它本身,但undefined不等于null

Object.is(null, null);              //true

Object.is(undefined,undefined);     //true
 
Object.is(undefined,null);          //false 

(5)比较正常对象

let test={a:1,b:2};     

let test1={a:2,b:5};
 
Object.is(test,test1)            //false
----------------------------------------------------------
let test={a:1,b:2};              //比较的两端需为一样的元素,当一边为变量时,另一边也必须是变量
 
Object.is(test,{a:1,b:2}).       //false
 
Object.is(test,test).            //true。

五.Object.keys()

Object.keys()方法返回一个由一个给定对象的自身可枚举属性组成的数组,数组的属性的排列顺序和使用for..in循环遍历该对象时返回的顺序一致两者的主要区别是 一个 for-in循环还会枚举其原型链上的属性

Object.keys(obj)

参数

obj:要返回其枚举自身属性的对象。

返回值:一个表示给定对象的所有可枚举属性的字符串数组。

描述:Object.keys返回一个所有元素为字符串的数组,其元素来自于从给定的对象上面可直接枚举的属性。这些属性的顺序与手动遍历该对象属性时的一致。

(1)数组Array对象(返回索引值)

let arr=[1,2,3];            
Object.keys(arr)         //  ["0", "1", "2”]

(2)object对象(返回key值)

let obj = { foo: "bar", baz: 42 };
Object.keys(obj)        //  ["foo", "baz”]

(3)类数组,对象

let obj = { 0 : "a", 1 : "b", 2 : "c”};
Object.keys(obj)       // ["0", "1", "2"]

(4)类数组对象 随机key排序

let Obj = { 100: 'a’, 2: 'b’,7: 'c’ };
console.log(Object.keys(Obj));     // ['2', '7', '100’]. 返回从小到大排序后的结果

六.obj.hasOwnProperty(key) 检测对象有没有指定的key

hasOwnProperty()方法会返回一个布尔值,指示对象是否具有指定的属性作为自身(不继承)属性。 obj.hasOwnProperty(prop)

参数

prop:要检测的属性 字符串 名称或者 Symbol

返回值:用来判断某个对象是否含有指定的属性的 Boolean

所有继承了 Object 的对象都会继承到 hasOwnProperty 方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和 in 运算符不同,该方法会忽略掉那些从原型链上继承到的属性。

let obj=new Object();
    obj.a=5;
    
console.log(obj.hasOwnProperty('a’))       // true
delete obj.a;
console.log(obj.hasOwnProperty(‘a’)).     //false 

七、Object.values()

方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for..in循环相同,返回的对象的value值,与Object.key()相反

例: (1)正常对象

let obj={a:1,b:2,c:3};
console.log(Object.values(obj))         //  [1, 2, 3]

(2)类数组对象

let obj ={0:'a',1:'b',2:'c'};
console.log(Object.values(obj)).       //  a,b,c

(3) key值为无序number

let obj={100:'a',10:'b',1:'1'};
console.log(Object.values(obj)).   // ["1", "b", "a"]

八、...扩展运算符

对象的扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。

let z = { a: 3, b: 4 };
let n = { ...z };     //  n =>  { a: 3, b: 4 }

由于数组是特殊的对象,所以对象的扩展运算符也可以用于数组。

let foo = { ...['a', 'b', 'c'] };  //  foo=> {0: "a", 1: "b", 2: "c"}foo

对象的扩展运算符等同于使用Object.assign()方法。可复制对象

let aClone = { ...a };
// 等同于
let aClone = Object.assign({}, a);

-------

let z = { a: 3, b: 4 };
let n = { ...z };  // n => {a: 3, b: 4}
n.a=8
console.log(z)  // {a: 3, b: 4}

九、Object.defineProperty的作用

今天有看了一篇面试的文章,其中有一题就问到Object.defineProperty的作用,说实话,如果问我,我也答不上来,随即百度找到了解析,然后就顺便添加到此次笔记中,以便以后查看。

语法: Object.defineProperty(obj, prop, descriptor)

参数说明:

obj:必需。目标对象 
prop:必需。需定义或修改的属性的名字
descriptor:必需。目标属性所拥有的特性

针对对象到属性,我们可以给这个属性设置一些特性,属性的值是否可以被重写?比如是否只读?;是否可以被for..inObject.keys()遍历,是否可以删除目标属性或是否可以再次修改属性的特性?

带着上面的问题,我们来看看Object.defineProperty能给我们带来哪些神奇的效果(数据描述中的属性都是可选的)!

1. value :属性对应的值,可以使任意类型的值,默认为undefined

let obj = {}
//第一种情况:不设置value属性
Object.defineProperty(obj,"newKey",{

});
console.log( obj.newKey );  //undefined

------------------------------

//第二种情况:设置value属性
Object.defineProperty(obj,"newKey",{
    value:"hello"
});
console.log( obj.newKey );  //hello

2. writable :属性的值是否可以被重写(默认为false)。设置为true可以被重写;设置为false,不能被重写。

let obj = {}
//第一种情况:writable设置为false,不能重写。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false
});

//更改newKey的值
obj.newKey = "change value";
console.log( obj.newKey );  //hello
------------------------------------
//第二种情况:writable设置为true,可以重写
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:true
});

//更改newKey的值
obj.newKey = "change value";
console.log( obj.newKey );  //change value

3. enumerable :此属性是否可以被枚举(使用for...in或Object.keys())。设置为true可以被枚举;设置为false,不能被枚举。(默认为false)。

var obj = {}
//第一种情况:enumerable设置为false,不能被枚举。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false
});

//枚举对象的属性
for( var attr in obj ){
    console.log( attr );  
}
//第二种情况:enumerable设置为true,可以被枚举。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:true
});

//枚举对象的属性
for( var attr in obj ){
    console.log( attr );  //newKey
}

4.configurable :是否可以删除目标属性或是否可以再次修改属性的特性(writable, configurable, enumerable)。设置为true可以被删除或可以重新设置特性;设置为false,不能被可以被删除或不可以重新设置特性。默认为false

这个属性起到两个作用:

    目标属性是否可以使用delete删除
    
    目标属性是否可以再次设置特性
//-----------------测试目标属性是否能被删除------------------------
var obj = {}
//第一种情况:configurable设置为false,不能被删除。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false,
    configurable:false
});
//删除属性
delete obj.newKey;
console.log( obj.newKey ); //hello

//第二种情况:configurable设置为true,可以被删除。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false,
    configurable:true
});
//删除属性
delete obj.newKey;
console.log( obj.newKey ); //undefined

//-----------------测试是否可以再次修改属性/特性------------------------
var obj = {}
//第一种情况:configurable设置为false,不能再次修改属性/特性。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false,
    configurable:false
});

//重新修改属性/特性
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:true,
    enumerable:true,
    configurable:true
});
console.log( obj.newKey ); //报错:Uncaught TypeError: Cannot redefine property: newKey

//第二种情况:configurable设置为true,可以再次修改属性/特性。
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:false,
    enumerable:false,
    configurable:true
});

//重新修改属性/特性
Object.defineProperty(obj,"newKey",{
    value:"hello",
    writable:true,
    enumerable:true,
    configurable:true
});
console.log( obj.newKey ); //hello

除了可以给新定义的属性设置属性/特性,也可以给已有的属性设置属性/特性.

//定义对象的时候添加的属性,是可删除、可重写、可枚举的。
var obj = {
    test:"hello"
}

//改写值
obj.test = 'change value';

console.log( obj.test ); //'change value'

Object.defineProperty(obj,"test",{
    writable:false
})


//再次改写值
obj.test = 'change value again';

console.log( obj.test ); //依然是:'change value'

提示:一旦使用Object.defineProperty给对象添加属性,那么如果不设置属性的特性,那么configurableenumerablewritable这些值都为默认的false

var obj = {};
//定义的新属性后,这个属性的特性中configurable,enumerable,writable都为默认的值false
//这就导致了neykey这个是不能重写、不能枚举、不能再次设置特性
//
Object.defineProperty(obj,'newKey',{

});

//设置值
obj.newKey = 'hello';
console.log(obj.newKey);  //undefined

//枚举
for( var attr in obj ){
    console.log(attr);
}

参考资料:传送门


    作者:晴天de雨滴    
    出处:https://juejin.im/post/6844903512493539341
    版权所有,欢迎保留原文链接进行转载:) 

如果你对我对文章感兴趣或者有些建议想说给我听👂,也可以添加一下微信哦!

如果亲感觉我的文章还不错的话,可以一下添加关注哦!

gold-cdn.xitu.io/2017/11/19/15fd4be41482278c?w=1000&h=800&f=png&s=204515)

最后:
        祝各位工作顺利!
                        -小菜鸟Christine