【ES新特性三】Object 原型、原型链相关方法

一、Object 原型、原型链相关方法

1.1 静态方法(Object 调用):

  • Object.setPrototypeOf(obj,prototype) 方法用于设置某个实例对象的原型(可以是null,也可以是一个对象)

  • Object.getPrototypeOf(obj)

​ 注:ES5之前,实例化对象通过 __proto__ 属性来获取原型对象;ES5中,不推荐以 __ 开头的语句,所以提供了 getPropertyOf() 方法用于获取对象的原型对象

原生JavaScipt案例合集
JavaScript +DOM基础
JavaScript 基础到高级
Canvas游戏开发

1.2 原型方法(实例调用)

此方法为 Object.prototype 原型方法,Object.prototype 作为任意对象在原型链上的顶级原型对象,在 JS 中定义的任意一个对象(自定义对象、数组对象等都可以调用子方法)。

  • isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。
  • hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
<body>
<ul>
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>
</body>
<script>
    // 获取类数组对象
    var lis = document.getElementsByTagName("li");

    // 定义数组
    var arr = [10,20,30,40];

    // 自定义对象
    var obj = {name:"张三"}

    // 查看Array.prototype这个原型是哪个实例化对象的原型
    console.log(Array.prototype.isPrototypeOf(lis));//false
    console.log(Array.prototype.isPrototypeOf(obj));//false
    console.log(Array.prototype.isPrototypeOf(arr));//true

    // 查找过程中,会查找整个原型链
    console.log(Object.prototype.isPrototypeOf(lis));//true
    console.log(Object.prototype.isPrototypeOf(obj));//true
    console.log(Object.prototype.isPrototypeOf(arr));//true

    // 获取实例化对象 arr 的原型对象
    // 原始方式
    console.log(arr.__proto__);//[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
    // ES5方式
    console.log(Object.getPrototypeOf(arr));//[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
    // 对比
    console.log(arr.__proto__ === Object.getPrototypeOf(arr));//true

   
    // 设置实例化对象 arr 的原型对象  可以为null 也可以是一个对象  会修改整个原型链
    Object.setPrototypeOf(arr,null);
    console.log(Object.getPrototypeOf(arr));//null

    Object.setPrototypeOf(arr,{a:10});
    console.log(Object.getPrototypeOf(arr));//{a:10}

    // 构造函数的原型不变  只是实例化对象原型指向改变
    console.log(Array.prototype);//[constructor: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, find: ƒ, …]
</script>
  • __proto__ 属性是一个访问器属性(一个 getter 函数和一个 setter 函数), 暴露了通过它访问的对象的内部[[Prototype]] (一个对象或 null)。proto 属性也可以在对象文字定义中使用对象 [[Prototype]] 来创建,作为Object.create() 的一个替代。

1.3 操作属性的方法(Object 调用)

**Object.getOwnPropertyNames()**方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括 Symbol 值作为名称的属性)组成的数组。

Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。

Object.hasOwn() 如果指定的对象自身有指定的属性,则返回 true。如果属性是继承的或者不存在,该方法返回 false

备注: Object.hasOwn() 旨在取代 Object.hasOwnProperty()

// 类数组对象
var obj = { 0: "a", 1: "b", 2: "c"};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]

// 数组对象
var arr = ["a", "b", "c"];
console.log(Object.getOwnPropertyNames(arr).sort()); // ["0", "1", "2", "length"]

// Symbols 
var obj = {};
var a = Symbol("a");
var b = Symbol.for("b");
obj[a] = "localSymbol";
obj[b] = "globalSymbol";
var objectSymbols = Object.getOwnPropertySymbols(obj);

// hasOwn()
const example = {};
Object.hasOwn(example, 'prop');   // false - 'prop' has not been defined

example.prop = 'exists';
Object.hasOwn(example, 'prop');   // true - 'prop' has been defined

二、Object 相关方法

2.1 Object.preventExtensions() 取消对象可拓展性

Object.preventExtensions(obj) 方法用于取消对象的可拓展性

Object.isExtensible(obj) 判断对象是否取消了可拓展性。返回值是一个布尔值,返回true对象可以拓展属性 返回false对象不可拓展属性

注:当一个对象被取消了可拓展性之后,对象不能再拓展属性,但是可以修改和删除属性

var obj = {
    a:1,
    b:2
}

console.log("取消对象可拓展性前对象:",obj);

// 取消对象拓展
Object.preventExtensions(obj);

// 拓展属性
obj.c = 3;

// 删除属性
delete obj.a;

// 修改属性值
obj.b = 22;

// 判断对象是否可拓展
console.log(Object.isExtensible(obj));//true

console.log("取消对象可拓展性后并操作属性后的对象:",obj);

2.2 Object.seal() 封闭对象

Object.seal(obj) 封闭对象的属性

Object.isSealed(obj) 判断对象属性是否被封闭。返回值布尔值,返回true 对象被封闭 返回false对象没有被封闭

注:当一个对象被封闭后,不能拓展和删除属性,但是可以修改属性。

var obj = {
    a:1,
    b:2
}

console.log("封闭前对象:",obj);

// 封闭对象
Object.seal(obj);

// 拓展属性
obj.c = 3;

// 删除属性
delete obj.a;

// 修改属性值
obj.b = 22;

// 判断对象是否被封闭
console.log(Object.isSealed(obj));//true

console.log("封闭后并操作属性后的对象:",obj);

2.3 Object.freeze() 冻结对象

Object.freeze(obj) 方法用于冻结对象的属性

Object.isFrozen(obj) 方法用于判断对象属性是否被冻结。返回值 一个布尔值 返回true对象被冻结,返回false对象没有被冻结

注:当一个对象被冻结后,不能拓展、修改和删除对象的属性

var obj = {
    a:1,
    b:2
}

console.log("冻结前对象:",obj);

// 冻结对象
Object.freeze(obj);

// 拓展属性
obj.c = 3;

// 删除属性
delete obj.a;

// 修改属性值
obj.b = 22;

// 判断对象是否被冻结
console.log(Object.isFrozen(obj));//true

console.log("冻结后并操作属性后的对象:",obj);

2.4 Object.create() 创建对象

Object.create(proto,[ propertiesObject ]) 方法使用现有对象作为新创建的对象的原型来创建新对象。

  • 参数

    • proto 该对象应该是新创建对象的原型。可以是null
    • propertiesObject 可选的。指定要添加到新创建的对象的属性描述符,以及相应的属性名称。这些属性对应于的第二个参数Object.defineProperties()对象属性的特性。
  • 返回值 具有指定原型对象和属性的新对象。

// 学过的创建对象的方式
// var obj = {};
// var obj1 = new Object();
// var obj2 = Object();

// ES5新增创建一个空对象  第一个参数新创建对象的原型设置为null
// var obj3 = Object.create(null);
// console.log(obj3)

//使用Object.create() 来创建一个对象,第一个参数原型对象为一个常量对象
// var obj = Object.create({
//     sayHi:function(){
//         console.log("Hello");
//     }
// })

// 第一个参数为空,第二个参数为 要创建对象的空对象的属性特性描述(类似于Object.defineProperty()设置的对象特性)
// var obj = Object.create(null,{
//     name:{
//         // 配置值
//         value:"张三",
//         // 配置是否可写
//         writable:false,
//         // 配置是否可枚举
//         enumerable:false
//     },
//     age:{
//         // 配置值
//         value:10,
//          // 配置是否可写
//          writable:false,
//     }
// })
// console.log(obj);
// console.log(obj.name);
// // 通过这种对象特性的方式创建的对象,默认属性不能被删除 修改
// obj.name = "haha";
// delete obj.age;
// console.log(obj);
// console.log(obj.name);


// 创建一个对象,并能继承另外一个对象的方法;将一个对象作为另外一个对象的原型
// 创建需要的原型对象
var prototype = {
    sayHi:function(){
        console.log("Hello");
    }
}

// 创建需要的特性属性对象
var options = {
    name:{
        // 配置值
        value:"张三",
        // 配置是否可写
        writable:false,
        // 配置是否可枚举
        enumerable:false
    },
    age:{
        // 配置值
        value:10,
         // 配置是否可写
         writable:false,
    }
}

// 两者组合创建对象
var obj = Object.create(prototype,options);

console.log(obj)//查看原型
obj.sayHi();

2.5 Object.create()完善继承

// 定义父类
function People(name,sex,age){
    this.name = name;
    this.sex = sex;
    this.age = age;
}

// 原型中定义方法
People.prototype.sayHi = function(){
    return "姓名:" + this.name + ",性别:" + this.sex + ",年龄:" + this.age;
}

People.prototype.sayHello = function(){
    return "Hello";
}

// 定义子类
function Student(name,sex,age,score){
    // applay实现继承(改变调用对象
    People.apply(this,arguments);

    // 定义子类拓展的属性
    this.score = score;
}

// var p = new People();
// delete p.name;
// delete p.sex;
// delete p.age;

// 子类继承父类中的方法 必须要使用原型继承 将子类的原型指向父类的实例
// Student.prototype = new Student();

// 使用Object.create()优化继承
Student.prototype = Object.create(People.prototype);

// 原型继承会造成结构的紊乱,将原型对象的构造函数手动改回到Student
Student.prototype.constructor = Student;

// 实例化对象
var s = new Student("张三","男",23,100);

// 调用父类原型中的方法
console.log(s.sayHi());

/*
* 原型继承,子类的原型就是父类的实例,这种方式会在子类的原型中多出几个无用的属性
* 此时,会在子类的原型中多出几个属性:name:undefined,age:undifined,sex:undefined
* 如果不考虑寄生组合继承这种方式进行优化,ES5还提供了Object.create()方法来优化
**/

2.6 封装实现Object.create()方法

// 定义父类
function People(name,sex,age){
    this.name = name;
    this.sex = sex;
    this.age = age;
}

// 原型中定义方法
People.prototype.sayHi = function(){
    return "姓名:" + this.name + ",性别:" + this.sex + ",年龄:" + this.age;
}

People.prototype.sayHello = function(){
    return "Hello";
}

// 定义子类
function Student(name,sex,age,score){
    // applay实现继承(改变调用对象
    People.apply(this,arguments);

    // 定义子类拓展的属性
    this.score = score;
}

// 取消Object.create方法
Object.create = null;
// 重新自定义 create 方法,实现相同的功能
Object.create = function(prototype){
    // 定义一个构造函数
    var F = function(){

    }

    // 将F的原型指向传入的原型
    F.prototype = prototype;

    // 返回F的实例
    return new F();
}

// 使用Object.create方法实现继承
Student.prototype = Object.create(People.prototype);

// 实例化对象
var s = new Student("张三","男",23,100);
console.log(s.sayHi());

2.7 Object.entries() 等返回可枚举数组方法

Object.entries() 方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致。

Object.values() 方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用 for...in 循环的顺序相同(区别在于 for-in 循环枚举原型链中的属性)。

const info = {
	name: "Jack",
    country: "Russia",
    age: 35
}

// 使用 Object.keys() 将对象所有自身可枚举属性收集到一个新数组中
const keys = Object.keys(info);

// 使用 Object.values() 将对象所有自身可枚举值收集到一个新数组中
const values = Object.values(info);

// 使用 Object.entries() 将对象所有自身可枚举所有键值对收集到一个新数组中
const keyVals = Object.entries(info)
// 使用 for...of 遍历自身的键值对(key 和 value)
for (const [key, value] of keyVals) {
  console.log(`${key}: ${value}`);
}

2.8 Object.assign()

Object.assign(target, …sources) 方法将所有可枚举Object.propertyIsEnumerable() 返回 true)的自有Object.hasOwnProperty() 返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象。如果目标对象与源对象具有相同的 key,则目标对象中的属性将被源对象中的属性覆盖

参数: target 目标对象,接收源对象属性的对象,也是修改后的返回值。

​ sources 源对象,包含将被合并的属性。

返回值:目标对象

注意点

  1. 针对 深拷贝, 需要使用其他办法,因为 Object.assign() 只复制属性值。假如源对象是一个对象的引用,它仅仅会复制其引用值(地址)。
  2. 原型链生的属性以及不可枚举属性不能被复制
'use strict';

// Object.assign() 属于浅拷贝
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { "a": 0, "b": { "c": 0}}

obj1.a = 1;
console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 0}}
console.log(JSON.stringify(obj2)); // { "a": 0, "b": { "c": 0}}

obj2.a = 2;
console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 0}}
console.log(JSON.stringify(obj2)); // { "a": 2, "b": { "c": 0}}

obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { "a": 1, "b": { "c": 3}}
console.log(JSON.stringify(obj2)); // { "a": 2, "b": { "c": 3}}

// 深拷贝(深度克隆)
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { "a": 0, "b": { "c": 0}}


// 合并对象
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };

const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, target object itself is changed.


//合并相同属性的对象
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };

const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

2.9 Object.is() 判断两个值是否为同一个值。

Object.is(value1, value2) 方法判断两个值是否为同一个值,如果满足以下任意条件则两个值相等:

  • 都是 undefined
  • 都是 null
  • 都是 true 或都是 false
  • 都是相同长度、相同字符、按相同顺序排列的字符串
  • 都是相同对象(意味着都是同一个对象的值引用)
  • 都是数字且
    • 都是 +0
    • 都是 -0
    • 都是 NaN
    • 都是同一个值,非零且都不是 NaN

Object.is()== 不同。== 运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将 "" == false 判断为 true),而 Object.is 不会强制转换两边的值。

Object.is()=== 也不相同。差别是它们对待有符号的零和 NaN 不同,例如,=== 运算符(也包括 == 运算符)将数字 -0+0 视为相等,而将 Number.NaNNaN 视为不相等。

// Case 1: Evaluation result is the same as using ===
Object.is(25, 25);                // true
Object.is('foo', 'foo');          // true
Object.is('foo', 'bar');          // false
Object.is(null, null);            // true
Object.is(undefined, undefined);  // true
Object.is(window, window);        // true
Object.is([], []);                // false
var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo);              // true
Object.is(foo, bar);              // false

// Case 2: Signed zero
Object.is(0, -0);                 // false
Object.is(+0, -0);                // false
Object.is(-0, -0);                // true
Object.is(0n, -0n);               // true

// Case 3: NaN
Object.is(NaN, 0/0);              // true
Object.is(NaN, Number.NaN)        // true

猜你喜欢

转载自blog.csdn.net/qq_39335404/article/details/132575784