一、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() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
xml
<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()
。
ini
// 类数组对象
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对象不可拓展属性
注:当一个对象被取消了可拓展性之后,对象不能再拓展属性,但是可以修改和删除属性
javascript
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对象没有被封闭
注:当一个对象被封闭后,不能拓展和删除属性,但是可以修改属性。
javascript
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对象没有被冻结
注:当一个对象被冻结后,不能拓展、修改和删除对象的属性
javascript
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()对象属性的特性。
-
返回值 具有指定原型对象和属性的新对象。
javascript
// 学过的创建对象的方式
// 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()完善继承
javascript
// 定义父类
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()方法
javascript
// 定义父类
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 循环枚举原型链中的属性)。
javascript
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 源对象,包含将被合并的属性。
返回值:目标对象
注意点:
- 针对 深拷贝, 需要使用其他办法,因为
Object.assign()
只复制属性值。假如源对象是一个对象的引用,它仅仅会复制其引用值(地址)。 - 原型链生的属性以及不可枚举属性不能被复制
ini
'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) 方法判断两个值是否为同一个值,如果满足以下任意条件则两个值相等:
Object.is()
与 ==
不同。==
运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将 "" == false
判断为 true
),而 Object.is
不会强制转换两边的值。
Object.is()
与 ===
也不相同。差别是它们对待有符号的零和 NaN 不同,例如,===
运算符(也包括 ==
运算符)将数字 -0
和 +0
视为相等,而将 Number.NaN
与 NaN
视为不相等。
dart
// 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