1、对象属性
分类
- 数据属性
- 存储属性
相关方法
Object.defineProperty(obj, 属性, {
// 数据属性
configuration: false,
enumerable: false,
writable: false,
value: xx,
// 存储属性
configuration: false,
enumerable: false,
set function(val){ 属性 = val },
get function(){ return obj[属性] }
// 通过 let obj = { name: 'zhangsna' }; obj.age = 12; 方式增加属性,这些属性的属性描述符都是 true (configuration、enumerable、writable)
// 对象的原型里面的 constructor 属性的默认属性描述符(enumerable 为 false)(configuration、writable 为 true)
})
Object.getOwnPropertyDescriptor(obj,属性名字) // 对象某一属性的属性描述符
Object.getOwnPropertyDescriptors(obj) // 获取对象的所有属性的属性描述符
Object.preventExtensions(obj) // 禁止属性里面增加属性
Object.freeze(obj) // 禁止对象一级属性的修改 (把对象的所有一级属性的属性描述符 writable = false)
Object.seal(obj) // 禁止对象一级属性的删除 (把对象的所有一级属性的属性描述符 configuration= false)
2、THIS
类型
- new
function Father() {
this.name = 'zhangsan'; // 约等于 obj.name = 'zhangsan'
}
let obj = new Father(); // obj 中的 this 指向 obj
- call、bind、apply // 显示改变 this 指向; 若不指定 this 则this 为全局作用域中的 this
- obj.fn() // fn 中的 this 指向调用者 obj
- 全局作用域中的默认 this
浏览器环境中为 window
node 环境为 {}
- 内置函数中的默认 this
setTimeOut() 全局作用域中的 this
监听时间、点击事件中的 this 指向对应的 dom 结点
- 箭头函数中的 this 指向定义箭头函数时的外部作用域中的 this
重写 call bind apply
Array.prototype.slice.call(arguments)
Function.prototype.mycall = function(newThis,...arg) {
newThis = newThis ?? window;
let fn = this;
newThis.fn = fn;
let result = newThis.fn(...arg);
delete newThis.fn
return result
}
Function.prototype.myApply= function(newThis,argArr) {
newThis = newThis ?? window;
argArr = argArr || [];
let fn = this;
newThis.fn = fn;
let result = newThis.fn(...argArr );
delete newThis.fn
return result
}
Function.prototype.mycall = function(newThis,...argA) {
let fn = this;
newThis = newThis ?? window;
function proxyFn(...argB) {
newThis.fn = fn;
let result = newThis.fn(...[...argA, ...argB])
delete newThis.fn;
return result
}
return proxyFn;
}
3、原型
介绍
Function Father(name,arr) {
this.name = name;
this.arr = arr;
this.commonString = 'commonString';
this.commonArr = ['commonArr'];
this.commonFunction = () => {};
}
let father1 = new Father();
// 此时 father1.__proto__ ==== Father.prototype;
// Father 的 prototype 类似于 {constructor: Father}
分类
- 对象的原型 (隐式原型)
- 获取方法
obj.__proto__ // 浏览器的方法
Object.getPrototypeOf(obj)
- 对象的隐式原型是其构造函数的显示原型
let obj = {};
obj.__proto__ === Object.prototype // true
Object.prototype.__proto__ === null
- 函数的原型 (显示原型、隐式原型)
fn.prototype // 显示原型
fn.__proto__ // 隐式原型 浏览器的方法
原型链
查找对象属性的路径:对象本身(该对象的构造函数身上) 》对象构造函数的原型身上 》…》null
其他
let o = {};
let obj = Object.create(o)
//此时 obj.__proto__ ==== o; 类似于 Object.setPrototype(obj, o);
4、创建对象模式
- 工厂模式
function Factory(name, age) {
return {
name,
age,
commonArr: [1,2];
commonFn() {
}
}
}
const p1 = Factory();
const p2 = Factory();
// 缺点
- p1、p2 都是对象类型,不能变成想要的类型。构造函数可以解决这一问题。
- 每创建一个实例公共方法都会再创建一次,造成存储浪费。原型可以解决这一问题。
- 构造函数模式
function Father(name, age) {
return {
name,
age,
commonArr: [1,2];
commonFn() {
}
}
}
const p1 = new Father();
const p2 = new Father();
// 创建出来的实例都有自己的类型为 Fahther 而不是通用的 Object
// 依然存在内存浪费问题
- 原型链模式
function Father(name, age) {
return {
name,
age,
}
}
Father.prototype = {
commonArr: [1,2];
commonFn() {
}
}
Object.defineProperty(Fahter.prototype, 'constructor', {
configuration: true,
enumerable: false,
writeable: true,
value: Father,
})
// 把公共方法放到原型中解决了内存浪费问题。
5、继承
- 原型链继承(子构造函数的原型为父构造函数的实例)
Student 实例查找属性的方式 Student 的构造函数 》 Person 的构造函数 (Student 的构造函数) 》Person 的原型 》 … 》null
缺点:不能实现多类继承;不能向父构造函数传参;当 Student 实例 s1 中没有某属性 xx 时,给该 s1.xx 赋值,若 xx 为引用数据类型并且父构造函数中有该属性,此时修改 xx 会影响到其他 Student 的实例(个人觉得第三点不是原型继承特有的)。 - 组合时继承:构造函数继承: 在Son构造函数内部执行:Father.call(this,参数) + 原型链继承
① 父构造函数至少调用两次: 子类原型时候使用+构造函数继承时候用
②子类实例会拥有两份父类的属性:子类实例自己里面的+子类原型对象中的 - 寄生式继承
缺点: 类似于工厂函数。 - 最佳选择: 寄生的 Object.create + 构造函数 new + 组合式子构造函数中调用父构造函数
class
const outerJMethod = () => {
console.log('外部箭头函数',this)
}
function outerFMethod () {
console.log('外部function定义的函数', this)
}
class MyClass {
static myStaticProp = 42;
instanceProp = '实例属性';
constructor() {
this.cInstanceProp = '构造函数中的实例属性'
// 所有实例共享同一片空间,但箭头函数中的 this 指向定义时候的外部作用域,普通函数中 this 指向调用者
this.cOuterJMethod = outerJMethod;
this.cOuterFMethod = outerFMethod;
this.cInstanceJMethod = ()=>{
console.log('构造函数中的实例箭头方法', this); // 构造函数中的 this 是 new 出来的新对象(实例),箭头函数中的 this 指向定义是的外部作用域也就是实例
}
this.cInstanceFMethod = function() {
console.log('构造函数中的实例箭头方法', this); // 构造函数中的 this 是 new 出来的新对象(实例),箭头函数中的 this 指向定义是的外部作用域也就是实例
}
};
// 实例方法
instanceJProp = ()=>{
console.log('实例箭头方法', this)
};
// 类的原型方法
instanceFProp() {
console.log('实例function方法', this)
};
}
const p1 = new MyClass();
const p2 = new MyClass();
class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
- 类的属性方法、实例的属性方法、类原型的属性方法;
- 类中用
static
修饰的属性或方法只能用类本身调用,且都是类的属性或方法,类的属性或方法只能是static
修饰的或者通过增加对象属性的方法例如类名.xx = xxx
增加或修改; - 类中没有修饰符修饰的属性都是实例属性,没有修饰符修饰的方法中箭头函数是实例方法而非箭头函数构造的则是类的原型方法;
- 类中构造器用
this.xx = xxx
的都是实例的属性或方法,构造器中的 this 指向的是新的实例。
小技巧
-
Reflect.set(obj,(a = 1));
// 相当于
a=1;
Reflect.set(obj, 1);
-