JS的对象✅
用字面量创建一个对象和通过构造函数创建一个对象
//new
function Person(name, age) {
this.name = name; // 设置名字属性
this.age = age; // 设置年龄属性
}
const person1 = new Person("张三", 25);
//字面量
let person2 = {
name: 'xiaoming',
age: 10
}
//两者是等价的,但使用字面量这一方式更加流行
构造函数名的首字母要大写, 要用new调用。使用new操作符调用构造函数会执行以下操作:
1. 在堆中创建一个新对象
2. 这个新对象内部的[[Prototype]]特性被赋值为构造函数的prototype属性
3. 构造函数内部的this被赋值为这个在堆中创建的新对象
4. 执行构造函数内部的代码(给新对象添加属性)
5. 如果构造函数返回非空对象,则返回该对象,否则返回刚创建的新对象
把实例名理解成一个 “指向内存的指针” 至关重要。构造函数也是函数,构造函数和普通函数唯一的区别是调用的方法不同。任何函数只要用new调用就是构造函数。如果没有用new来调用函数,函数内部的this不会指向主存中的某一个新对象,此时this的指向不明确。在没有明确的this值的情况下(没有用new、allpy、call,也没有作为对象的方法调用),this指向把函数当成方法调用的上下文对象。
Class
class Person {
constructor(name, age) {
this.name = name; // 设置名字属性
this.age = age; // 设置年龄属性
}
// 原型方法
getInfo() {
return `${this.name} is ${this.age} years old.`;
}
// 静态属性
static nationality = '中国';
}
const person1 = new Person("张三", 25);
console.log(Person.nationality)
constructor关键字用于在类定义块内部创建类的构造函数,会为新创建的实例添加自有属性。
类语法把在类块中定义的方法作为原型上的方法,在类块中用static关键字声明的属性将作为类的静态属性。构造函数中添加的才是实例上的方法和属性,在使用new操作符创建类的新实例时,调用的是构造函数函数。
如果不使用new操作符实例化类会报错。用new操作符实例化类的操作等于用new调用其构造函数,执行步骤如下:
1. 在堆中创建一个新对象
2. 这个新对象内部的[[Prototype]]指针被赋值为构造函数的prototype属性
3. 构造函数内部的this被赋值为这个新对象
4. 执行构造函数内部的代码(给实例增添属性)
5. 如果构造函数返回非空对象则返回该对象,否则返回刚创建的对象。
请把类看成特殊的构造函数。声明一个类后用typeof操作符检测类标识符会表明他就是一个funtction。类标签符有prototype属性(其实是构造函数的prototype),而这个prototype指向的原型也有一个constructor属性指回类自身(其实还是这个类的构造函数)。
在ECMAScript 2022(ES13)中,我们可以在类里面定义私有变量:
class Person {
#age;
construct(name, age) {
this.name = name;
this.age = #age;
}
getAge() {
return this.#age;
}
}
console.log(new Person("Boyiao", 20)['#age']) // Error
也可以借助闭包来实现:
class Person {
constructor(name, age) {
this.name = name; // 公有变量
let privateAge = age; // 私有变量
// 定义私有方法
this.getAge = function() {
return privateAge; // 通过闭包访问私有变量
};
}
}
const person = new Person("王五", 40);
console.log(person.privateAge); // undefined
console.log(person.getAge()); // 40
原型
每个函数都会创建一个prototype属性,该属性是一个对象,包含应该由特定应用类型的实例共享的属性和方法。这个对象就是通过调用构造函数创建的对象的类原型。
无论何时只要创建一个函数,就会按照特定的规则为该函数床架你一个prototype属性,指向原型对象;每个原型对象自动获得一个名为constructor的属性,指回与之关联的函数。
定义构造函数时原型对象默认只会获得constructor属性,其他方法继承自Object。用构造函数创建的实例内部的[[Prototype]] 指针就会被赋值为构造函数的原型对象,脚本中没有访问这个特性的标准方式,但在FireFox、Safari和Chrome中每个对象上都暴露了__proto__属性,可以通过它访问对象的原型。
正常的原型链都会终止于Object对象。再通过对象访问属性时,会按照这个属性的名称顺着原型链展开查找。hasOwnProperty()通过实例调用,可以看到访问的属性是实例属性(返回true)还是原型属性;单独使用in操作符只要通过对象可以访问就返回true。
用defineProperty和Proxy来拦截对对象的操作
const apple = { color: 'red', name: 'apple' }
Object.defineProperty(apple, 'color', {
configurable: true, //是否可以通过delete删除并重新定义,是否可以修改他的特性,是否可以改为访问器属性
enumberable: true //是否可以通过for-in返回,默认是true
get: () => {
console.log('正在访问color属性');
return apple.color;
},
set: (newColor) => {
console.log('正在写color属性');
apple.color = newColor;
}
})
const appleProxy = new Proxy(apple, {
get(target, property) {
if(property === 'color') {
console.log('正在访问 color 属性');
}
return target[property]; // 返回实际属性值
}
set(target, property, value) {
if(property === 'color') {
console.log('正在写 color 属性');
}
target[property] = value;
return true;
}
})
手写深拷贝
function deepClone(obj, cache = new WeakMap()) {
if(obj === null || typeof obj !== 'object') { return obj; }
if(obj instanceof Date) { return new Date(obj); }
if(obj instanceof RegExp) { return new RegExp(obj); }
// 处理循环引用
if(cache.has(obj)) {
return cache.get(obj);
}
const newObj = Array.isArray(obj) ? [] : {};
cache.set(obj);
for(const key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key], cache);
}
}
return newObj;
}
原文地址:https://blog.csdn.net/YUYUYU972/article/details/143866225
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!