那些你不知道的对象的方法

1,669 阅读10分钟

👧文章是作者在回顾知识时,顺手整理的一些例子📜,希望可以帮到你们✨✨如果有问题欢迎指正。刚开始写文章,请大家多多支持💪 如果感觉不错的话欢迎点赞👍,欢迎大家在评论区交流👏

👣👣最近好多同事都离职了,找到了更好的工作,尤其对于今年这个特殊情况,加薪和跳槽都没有往年容易。所以我们得加倍的努力,时时刻刻为跳槽或者加薪做好准备💪💪 都说女孩儿不努力,只有两种结果,穿不完的地摊货,逛不完的菜市场😔😔,作为一个姑娘,心中也有一个大目标(说出来吓晕你。。。🤪🤪)。

对象的方法

Object.assign()

方法用于将所有可枚举的属性值从一个或者多个元对象复制到目标对象,然后返回目标对象.

const obj1 = { a:1, b:2, c:22 }
const obj2 = { c:3, d:4, name: '啦啦'}
const obj3 = Object.assign( obj1, obj2)
console.log(obj3) //{a: 1, b: 2, c: 3, d: 4, name: "啦啦"}
console.log(obj1) //{a: 1, b: 2, c: 3, d: 4, name: "啦啦"} 把原来的obj1也改变了

复制合并对象

不会修改之前的对象,且如果存在相同的属性,则后面的会覆盖前面的属性

const obj1 = { name: '张三', age: 20, }
const obj2 = { name: 'lisa', address: '陕西'}
const obj3 = Object.assign({},  obj1, obj2)
console.log(obj3) //{name: "lisa", age: 20, address: "陕西"}
console.log(obj1) //{name: "张三", age: 20} 没有修改

浅拷贝

let obj1 = { name: 'lisa', age: 15, other: {address: '陕西'}};
let obj3 = Object.assign({}, obj1)
console.log(obj3)
obj3.other.address = '西安'
console.log(obj1.other.address) //西安

将其他类型转化为对象

如果源对象出现非对象参数,这些对象都会转化为对象,如果无法转化为对象,则会跳过该参数 如果把null或者undefined放在目标对下个的位置,则会报错,因为null和undefined无法转化为对象。

const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo");
const obj  = Object.assign({},v1, null,v2,undefined,v3,v4)
console.log(obj) //{0: "a", 1: "b", 2: "c"}

如果传入的是参数,则会当成对象处理:

console.log(Object.assign([1, 2, 3], [4, 5])) //[4, 5, 3]
console.log(Object.assign([1, 2, 3], {a: 1,b: 2})) // [1, 2, 3, a: 1, b: 2]
console.log(Object.assign( {a: 1,b: 2}, [1, 2, 3])) //  {0: 1, 1: 2, 2: 3, a: 1, b: 2}

目标对象是数组,返回的就是数组,目标对象是对象,返回的就是对象;

console.log(Object.assign( 'abc')) //String {"abc"}
console.log(Object.assign( [1,2,3,4])) // [1, 2, 3, 4]
console.log(Object.assign( false,[1,2,3,4]))  //Boolean {false, 0: 1, 1: 2, 2: 3, 3: 4}
var aa = Object.assign( false,[1,2,3,4]); //Boolean {false, 0: 1, 1: 2, 2: 3, 3: 4}
var aa = Object.assign( false,[1,2,3,4]); // Symbol {Symbol(haha), 0: 1, 1: 2, 2: 3, 3: 4}
 

继承属性和不可枚举属性是不能拷贝的

const obj = Object.create({foo: 1}, { // foo 是个继承属性。
    bar: {
        value: 2  // bar 是个不可枚举属性。
    },
    baz: {
        value: 3,
        enumerable: true  // baz 是个自身可枚举属性。
    }
});

const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }

Object.create()

Object.create(proto[, propertiesObject])返回一个新对象,带着指定的原型对象和属性。

proto: 必须参数,新创建对象的原型对象。

propertiesObject: 可选参数,新对象的属性。

const o = Object.create({},{
    name: {
        value: 'lisa',
        writable: true,
        enumerable: true,
        configurable: true
    }
})
console.log(o)

和new Object()的区别:

  • 创建对象的方式不同

new Object() 通过构造函数来创建对象, 添加的属性是在自身实例下。 Object.create() es6创建对象的另一种方式,可以理解为继承一个对象, 添加的属性是在原型下 Object.create()方法创建的对象,属性是在原型下面的,但是对象也可以直接访问。

let person = { name: 'lisa'};
console.log(person.__proto__);
let  person2 = Object.create(person);
console.log(person2)
console.log(person2.__proto__)
  • 创建对象属性的性质不同
let  o = Object.create({a:20}, {p: {value: 42}});
console.log(o)
o.name = 'lisa';
for (let item in o){
    console.log(item) // name a
}
// 因为Object.create()用第二个参数创建的对象的属性描述符默认是false
console.log(Object.getOwnPropertyDescriptor(o,'p'))
  • 创建空对象不同
let a = {};
console.log(a)
console.log(Object.create(Object.prototype));
// 创建一个原型为null的空对象
let b = Object.create(null)
console.log(b)

Object.freeze()与Object.isFrozen()

Object.freeze() 方法可以冻结一个对象。一个对象一旦被冻结了,属性值为基本类型的属性就无法修改了,但是属性值是引用类型的依然可以修改。

如果要使数据完全冻结,不可以做任何修改,则需要递归冻结;

Object.freeze(o)
o.name = 'aa';
o.other.hello = '你好呀'
console.log(o.other.hello) //你好呀

Object.isFrozen() 判断一个对象是否被冻结。

// 一个对象默认是可扩展的,所以它也是非冻结的.
Object.isFrozen({}); // === false

Object.fromEntries()

将键值对列表转化为一个对象. Map 转化为Object

const map = new Map([['age',20],['name','崽崽']]);
console.log(map)
console.log(Object.fromEntries(map))

将数组转化为object

const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }

对象转换

const object1 = { a: 1, b: 2, c: 3 };

const object2 = Object.fromEntries(
  Object.entries(object1)
  .map(([ key, val ]) => [ key, val * 3 ])
);

console.log(object2);

Object.is()

判断两个值是否相同.

Object.is(null, null);       // true
Object.is(0, -0);            // false
Object.is(0, +0);            // true
Object.is(-0, -0);           // true
Object.is(NaN, 0/0);         // true

以下情况返回都是true:

  • 两个值都是undefined
  • 两个值都是null
  • 两个值都是true或者false
  • 两个值都是由相同个数的字符按照相同的顺序组成的字符串
  • 两个值指向同一个对象
  • 两个值都属数字,并且
    • 都是正零 +0
    • 都是负零 -0
    • 都是NAN
    • 都是除零和NAN外的其他同一个数字

Object.isExtensible() 与 Object.preventExtensions()

Object.isExtensible() 判断一个对象是否是可扩展的,也就是说判断是否可以在该对象上添加新的属性,结果返回一个布尔值。

let  obj = {name: 'haha'};
console.log(Object.isExtensible(obj))  //true

// 默认情况下对象是可扩展的,即可以给他们添加新的属性,以及修改原型
let obj2 = {name: 'opop'}

//冻结对象是不可扩展的
Object.freeze(obj2)
console.log(Object.isExtensible(obj2)) //不可扩展的

//密封对象是不可扩展的
let obj3 = {name: 'oppo'}
Object.seal(obj3)
console.log(Object.isExtensible(obj3)) //false

// ...可以变的不可扩展.
Object.preventExtensions(empty);
Object.isExtensible(empty); // === false

Object.preventExtensions() 将对象变为不可扩展的,并返回原对象。

默认情况下对象是可扩展的,也就说可以给对象添加属性,以及修改对象的原型。

let obj = {
    prop() {},
    foo: 'bar'
}
let obj2 = Object.preventExtensions(obj);
console.log(Object.is(obj,obj2)) //true
console.log(Object.isExtensible(obj)) //false

Object.seal()与Object.isSealed()

Object.seal() 方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。

const obj1 = {name: 'lisa'}
console.log(Object.seal(obj1)) //{name: "lisa"}
obj1.name = 'hobby';
obj1.age = 20;
console.log(obj1) // {name: "hobby"}

使用Object.freeze()冻结的对象中的现有属性值是不可变的。

Object.isSealed() 方法判断一个对象是否被密封。

// 新建的对象默认不是密封的.
var empty = {};
Object.isSealed(empty); // === false

Object.values()、 Object.keys()与 Object.entries()

Object.values() 返回参数对象自身的所有可遍历属性的键值

var obj = { 0: 'a', 10: 'b', 2: 'c' };
console.log(Object.values(obj)); //["a", "c", "b"]
var obj2 = { 0: 'a', 'abc': 'b', 2: 'c' };
console.log(Object.values(obj2)); //["a", "c", "b"]
var obj3 = {'name': 'lisa',[Symbol('address')]:'陕西', 10 :' age' }
console.log(Object.values(obj3)) //[" age", "lisa"]

Object.keys() 返回一个所有元素为字符串的数组(不包含Symbol()).顺序为 数字>字符串

var obj = { 0: 'a', 10: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '2', '10']
var obj2 = { 0: 'a', 'abc': 'b', 2: 'c' };
console.log(Object.keys(obj2)); // console: ['0', '2', 'abc']
var obj3 = {'name': 'lisa',[Symbol('address')]:'陕西', 10 :' age' }
console.log(Object.keys(obj3)) //["10", "name"]

Object.entries() 返回参数哦对象自身的所有可遍历属性的键值对


var obj = {name: 'lisa', age: 20}
console.log(Object.keys(obj))
console.log(Object.values(obj))
console.log(Object.entries(obj))
var obj = { foo: "bar", baz: 42 }; 
var map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }

对象的属性

属性的简洁表示法

ES6中如果属性名和属性值相等,可以不写属性值,只写属性名

let o = {
    name: '张三',
    age: 23,
    birth, //等同于birth: birth
}
let obj2 = {
    name: '张三',
    *go(){
      console.log('我是一个generator函数') //generator函数前面加*
    },
    getName: function(){ 
        console.log(this.name)
    }
    // 等同于  
    // getName(){
    //     console.log(this.name)
    // }
}

用表达式作为属性名,这时必须将表达式放在方括号里面

let obj2 = {
    ['na'+'me'] : '张三',
}
console.log(obj2) //{name: "张三"}
var arr = [1,2,3]
arr.forEach(item => {
    let obj3 = {
        [`name${item}`]: '张三'
    }
    console.log(obj3)
})

let obj4 = {
    [1+2*3]: '123'
}
console.log(obj4) //{7: "123"}

obj5 = {
    ['he'+'llo'](){
        console.log('hello')
    }
}
console.log(obj5)

属性名表达式与简介表达式不能同时使用

var foo = 'bar';
var bar = 'abc';
// var baz = { [foo] } 
var baz = {[foo]:'abc'}
console.log(baz)

如果属性表达式的属性名称是一个对象,则会自动将对象转化为字符串

const keyA = { a: 1};
const keyB = { b: 2};
const my = {
    [keyA]:'valueA',
    [keyB]: 'valueB'
}

console.log(my) //{[object Object]: "valueB"}

因为keyA和keyB转化为字符串都是 [object Object], 所以keyB会覆盖keyA console.log(JSON.stringify(keyA)) console.log(JSON.stringify(keyB))

属性的可枚举性和遍历

对象的可枚举性

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。

const obj = {
    name: '张三',
    age: 20,
    [`${1+23}`]:'66',
    address: '陕西',
    hobby: '游泳',
    [1 + 2]: '数字表达式',
    [Symbol()]:'haha',
    [Symbol('hello')]: 'hello'
}
console.log(Object.getOwnPropertyDescriptor(obj,3))
/**
{
  configurable: true
  enumerable: true //称为可枚举性,如果该属性的值为false,就表示该操作会忽略当前属性
  value: "张三"
  writable: true 
}
 */

ES6 规定,所有 Class 的原型的方法都是不可枚举的。

属性的遍历

const obj = {
    name: '张三',
    age: 20,
    [`${1+23}`]:'66',
    address: '陕西',
    hobby: '游泳',
    [1 + 2]: '数字表达式',
    [Symbol()]:'haha',
    [Symbol('hello')]: 'hello'
}

1. for ...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)

for(let item in obj){
    console.log(item)
}

把age设置为不可枚举属性:

const obj = {
    name: '张三',
    age: 20,
}
Object.defineProperties(obj, {
    'age':{
        enumerable: false
    }
})
for(let item in obj){
    console.log(item) //只有 => name
}

2. Object.keys(obj)

返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

console.log(Object.keys(obj)) // ["3", "24", "name", "age", "address", "hobby"]

3. Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

console.log(Object.getOwnPropertyNames(obj))  //  ["24", "name", "age", "address", "hobby"]

4. Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性的键名。

console.log(Object.getOwnPropertySymbols(obj))  //  [Symbol(), Symbol(hello)]

5. Reflect.ownKeys(obj)

Reflect.ownKeys返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。

console.log(Reflect.ownKeys(obj)) //["24", "name", "age", "address", "hobby", Symbol()]

综上所述:

  1. 首先便利所有数值键,然后按照数值升序排列;
  2. 其次遍历所有字符串,按照加入时间升序排列;
  3. 最后遍历所有Symbol键,按照加入时间升序排列;

proto

__proto__属性用来读取或者设置当前对象的prototype对象,它本质是一个内部属性,而不是一个正式对外开放的api,所以不推介使用。

 function test(){};
test.prototype.getName = function(){
    console.log(name)
}
let name = new test();
console.log(name)
console.log(name.__proto__)
console.log(test.prototype)

__proto__的读取器(getter)暴露了一个对象的内部 [[Prototype]] 。对于使用对象字面量创建的对象,这个值是 Object.prototype。对于使用数组字面量创建的对象,这个值是 Array.prototype。对于functions,这个值是Function.prototype

console.log(obj.__proto__)
console.log(Object.prototype)
const arr = new Array(10);
console.log(arr.__proto__)
console.log(Array.prototype)
let getName = function(){}
console.log(getName.__proto__)
console.log(Function.prototype)

Object.prototype.constructor

所有的对象都会从它的原型上继承一个constructor属性

let obj = {
    name: '123'
}
console.log(obj.constructor)  //Object
console.log(Object.prototype.constructor === obj.constructor)
let get = function(){};
const name  = new get();
console.log(name)
console.log(name.constructor)

var obj = {
    name: '123'
}
console.log(obj.constructor)  //Object
let obj2 = new Object();
console.log(obj2.constructor) //Object

let arr = [];
console.log(arr.constructor)
let  arr2 = new Array();
console.log(arr2.constructor)
let date = new Date();
console.log(date.constructor)

定义在构造函数原型属性上的属性和方法虽然不能直接表现在实例对象上, 但是实例对象却可以访问或者调用它们。

比如:

let get = function(){};
const o  = new get();
get.prototype.name = '悟空'
console.log(o.name) //悟空

Object.defineProperties()

方法直接在一个对象上定义新的属性或修改现有属性,并返回该对象。

const obj = {
    name: '张三',
    age: 20,
}
Object.defineProperties(obj, {
    'name':{
        writable:true
    },
    'age':{
        enumerable: false
    },
    'hobby': {
        value: '游泳'
    }

})

console.log(obj) //{name: "张三", age: 20, hobby: "游泳"}

Object.defineProperty()

方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 写法和上面的略微有点差别:

let o = {};
Object.defineProperty(o, 'name',{
    value : 37,
    writable : true,
    enumerable : true,
    configurable : true
})
var o = {};

o.a = 1;
// 等同于:
Object.defineProperty(o, "a", {
  value: 1,
  writable: true,
  configurable: true,
  enumerable: true
});


// 另一方面,
Object.defineProperty(o, "a", { value : 1 });
// 等同于:
Object.defineProperty(o, "a", {
  value: 1,
  writable: false,
  configurable: false,
  enumerable: false
});

Object.getOwnPropertyDescriptor()

方法返回指定对象上一个自有属性对应的属性描述符。

Object.getOwnPropertyDescriptor(obj, prop)
let o = {
    name: 'haha'
}
let d = Object.getOwnPropertyDescriptor(o,'name')
console.log(d)

let obj = {};
Object.defineProperty(obj, 'name',{
    value: '崽崽',
    enumerable: false
})
let d2 = Object.getOwnPropertyDescriptor(obj,'name')
console.log(d2)

Object.getOwnPropertyNames()

返回一个由指定对象的所有自身属性组成的数组

console.log(Object.getOwnPropertyNames(obj)) // ["name", "address"]
const arr = ['a','b','c','d'];
console.log(Object.getOwnPropertyNames(arr)) //["0", "1", "2", "3", "length"]
//类似数组的对象
const  obj2 = {0: 'a', 1: 'b', 2: 'c'}
console.log(Object.getOwnPropertyNames(obj2)) //["0", "1", "2"]
// Object.getOwnPropertySymbols()方法返回一个给定对象自身的所有Symbol属性的数组。
const obj3 = {
    name: 'haha',
    [Symbol('hello')]:'hello'
}
console.log(Object.getOwnPropertySymbols(obj3))  //[Symbol(hello)]

Object.getPrototypeOf()与 Object.setPrototypeOf()

Object.getPrototypeOf(object) 方法返回指定对象的原型,如果没有继承属性,则返回null。

let obj = {name: 'lisa', address:'陕西'}
console.log(Object.getPrototypeOf(obj))
console.log(obj.__proto__)
var o = {};
const o2 = Object.create(o);
console.log(o2)
console.log(Object.getPrototypeOf(o2) === o);

Object.getPrototypeOf(obj)和obj.__proto__打印的结果一样。

__proto__和prototype的区别:

1.对象有属性__proto__,指向该对象的构造函数的原型对象。

2.函数有属性prototype,prototype指向该方法的原型对象。

function  myFun (name){
    this.name = name;
};
myFun.prototype.getName =function (name) {
    console.log(this.name)
}
let mySelf = new myFun('华仔');
console.log(mySelf) //myFun {name: "华仔"}
mySelf.getName() //华仔

console.log(myFun.prototype) //{getName: ƒ, constructor: ƒ}
console.log(Object.getPrototypeOf(mySelf)) //{getName: ƒ, constructor: ƒ}
Object.prototype是构造出来的对象的原型

Object.setPrototypeOf(obj, prototype) 方法设置一个指定的对象的原型到另一个对象或 null。

把一个对象的原型设置为null:

let o = {};
console.log(Object.getPrototypeOf(o))
let m = Object.setPrototypeOf(o, null);
console.log(o) //{}  No properties

给一个对象设置指定的原型:

function Person(name) {
    this.name = name;
}
Person.prototype.sex = 'boy';
Person.prototype.getsex = function () {
    console.log(sex)
}
const person1 = new Person('lisa');
const person2 = {};
const prototype = Object.getPrototypeOf(person1);
Object.setPrototypeOf(person2, prototype)
console.log(person2)

本文使用 mdnice 排版