JS中的Object、Proxy、Reflect以及Promise对象
一、创建对象的方式
-
字面量
let a = {}
-
new关键字
let b = new Object()
-
Object.create()方法
let c = Object.create(Object.prototype)
二、function、Function、prototype、constructor理解
-
function与Function
-
通过function声明的函数,实际上都是通过Function实例化出来的,即每个js函数都是Function对象
- functin test(){} <=> var test = new Function()
-
new Function()创建函数
- 如果有参数,依次传入参数名字符串,最后传入函数体字符串
// 函数创建方式一:通过function声明 function test1(a, b) { console.log(a + b) } // 函数创建方式二:通过new Function(),或者省掉new关键字,直接Function() // 参数分开写 var test2 = new Function('a', 'b', 'console.log(a+b)') // 参数合在一起写 var test3 = Function('a, b', 'console.log(a+b)') test1(1, 2) // 3 test2(3, 5) // 8 test3(4, 6) // 10 console.log(test1.constructor === Function); // true => 说明每个js函数都是Function对象
- 如果有参数,依次传入参数名字符串,最后传入函数体字符串
-
-
prototype属性与constructor属性
- prototype是所有函数对象所拥有的一个特殊属性,普通对象没有
- 注意fun.__proto__ != fun.prototype,因为fun.__proto__ === Function.prototype,这样就方便理解了
- 实例对象的constructor属性,返回该实例对象的构造函数,注意不是字符串
- prototype是所有函数对象所拥有的一个特殊属性,普通对象没有
三、原型链
-
代码理解
// 1、原型 => 类 或 构造函数 // 2、原型对象 => 对象 // 3、实例对象 => 创建的实例 // 4、相互转化 // 原型 class Person { } console.log('原型:', Person) // 原型: [class Person] // 原型对象添加属性、方法 Person.prototype.username = '默认用户' Person.prototype.password = '默认密码' Person.prototype.login = function login(username, password) { console.log(`${username}登录成功!`) } console.log('原型对象:', Person.prototype) // 原型对象: { username: '默认用户', password: '默认密码', login: [Function: login] } // 原型对象 转 原型 console.log(Person.prototype.constructor === Person) // true // 创建实例对象 const person = new Person(); person.login('小李', '123456') // 小李登录成功! const person2 = new Person.prototype.constructor() console.log(person2.username) // 默认用户 // 实例对象 转 原型对象 console.log(person.__proto__ === Person.prototype) // true console.log(Object.getPrototypeOf(person) === Person.prototype) // true // 实例对象到原型 console.log(person.__proto__.constructor === Person) // true => 可以用来检验某对象是否是该类的实例对象
四、Object对象
-
常用方法
- Object.create():创建对象
- Object.is():判断两个对象是否是同一个对象
- Object.hasOwn():判断某个对象自身是否包含某个属性
- Object.getOwnPropertyDescriptor():获取指定对象某个自有属性描述符
- Object.getOwnPropertyDescriptors():获取指定对象所有自有属性描述符
- Object.getPrototypeOf():获取实例对象的原型对象
- Object.setPrototypeOf():设置某个实例对象的原型对象
- Object.getOwnPropertyNames():获取某个对象所有自身属性
- Object.getOwnPropertySymbols():获取某个对象所有自身符号属性
- Object.defineProperty():给对象定义或修改属性(存在局限性)
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>测试</title> <script> // 1、创建对象 let a = Object.create(Document.prototype) console.log(a.__proto__.constructor === Document) // true // 2、判断两个对象是否是同一个对象 // window、self、top、parent也是一个补环境检测点 console.log(Object.is(window, self)) // true console.log(Object.is(window, top)) // true console.log(Object.is(window, parent)) // true console.log(Object.is(window, self.top.parent)) // true // 3、判断对象自身是否含有某个属性 console.log(Object.hasOwn(document, 'location')) // true console.log(Object.hasOwn(document, 'cookie')) // false // 4、获取原型对象 console.log(Object.getPrototypeOf(document)) // HTMLDocument {Symbol(Symbol.toStringTag): 'HTMLDocument', onreadystatechange: undefined, onmouseenter: undefined, onmouseleave: undefined, constructor: ƒ} // 5、获取指定对象的指定属性描述符 console.log(Object.getOwnPropertyDescriptor(document, 'location')) // {enumerable: true, configurable: false, get: ƒ, set: ƒ} // 6、获取指定对象所有自有属性描述符 console.log(Object.getOwnPropertyDescriptors(document)) // {location: {…}} // 7、设置原型对象 let obj = {} Object.setPrototypeOf(obj, Document.prototype) console.log(obj.__proto__) // Document {…} // 8、获取自身所有属性 console.log(Object.getOwnPropertyNames(document)) // ['location'] // 9、获取自身所有符号属性 console.log(Object.getOwnPropertySymbols(Document.prototype)) // [Symbol(Symbol.toStringTag), Symbol(Symbol.unscopables)] </script> </head> <body> </body> </html>
-
Object.defineProperty()方法及属性描述符
// 1、初始化对象时定义属性 let user = { name: '小李' } // 2、添加属性 user.age = 28 // 3、Object.defineProperty() // 第一个参数是对象,第二个参数是属性,第三个参数是一个属性描述符(对象) Object.defineProperty(user, 'height', { enumerable: true, // 是否可遍历,设为false,则遍历时不会显示该属性 configurable: true, // 是否可配置,设为false,则后续继续调用Object.defineProperty定义该属性时会报错 writable: true, // 是否可写,设为false,后续修改该属性不会生效 value: 175 // 设置属性具体值 }) // 4、属性描述符中,(writable、value)和(get、set)不能同时使用 // 属性描述符第二种写法(推荐) let tmpWeight = 120 Object.defineProperty(user, 'weight', { enumerable: true, configurable: true, get: function (){ console.log('正在获取属性') return tmpWeight }, set: function (value){ console.log('正在设置属性') tmpWeight = value } }) user.weight = 130 // 设置属性 console.log(user.weight) // 获取属性
-
判断对象类型
- typeof
- Object.prototype.toString.call() -- 推荐
// 1、typeof console.log(typeof '1') // string console.log(typeof 1) // number console.log(typeof true) // boolean console.log(typeof {}) // object console.log(typeof []) // object 不容易区分 console.log(typeof null) // object 不容易区分 console.log(typeof undefined) // undefined console.log(typeof Symbol()) // symbol // 2、Object.prototype.toString.call console.log(Object.prototype.toString.call('1')) // [object String] console.log(Object.prototype.toString.call(1)) // [object Number] console.log(Object.prototype.toString.call(true)) // [object Boolean] console.log(Object.prototype.toString.call({})) // [object Object] console.log(Object.prototype.toString.call([])) // [object Array] console.log(Object.prototype.toString.call(null)) // [object Null] console.log(Object.prototype.toString.call(undefined)) // [object Undefined] console.log(Object.prototype.toString.call(Symbol())) // [object Symbol]
五、Reflect对象
-
简介
- Reflect对象时ES6为了操作对象,而提供的新API
- 把Object对象的一些方法放到Reflect对象上
- 修改某些Object对象的返回值,让其变得合理,比如defineProperty方法在无法定义属性时不会报错,而是返回false
- 让Object操作都变成函数行为,比如in和delete操作,变成了 Reflect.has(obj, name) 和 Reflect.delete(obj, name)
-
apply()
- Function.prototype.apply(thisArg, args)
// Reflect.apply(target, thisArg, args) const obj = { age: 28 } function fn(a, b, c){ return this.age + a + b + c } // 旧写法 console.log(fn.apply(obj, [1, 2, 3])) // 34 // 新写法 console.log(Reflect.apply(fn, obj, [1, 2, 3])) // 34
- Function.prototype.apply(thisArg, args)
-
construct()
- new 构造函数()
// Reflect.construct(target, argArray, newTarget) class Person{ constructor(name, age) { this.name = name this.age = age } } // 旧写法 const p1 = new Person('小李', 28) console.log(p1) // Person { name: '小李', age: 28 } // 新写法 const p2 = Reflect.construct(Person, ['小王', 30]) console.log(p2) // Person { name: '小王', age: 30 }
- new 构造函数()
-
get()
- obj.prop
// Reflect.get(target, prop, receiver) const obj = { name: '刘德华', age: 40, get info(){ return `${this.name}:${this.age}岁` } } console.log(Reflect.get(obj, 'name')) // 刘德华 console.log(Reflect.get(obj, 'age')) // 40 console.log(Reflect.get(obj, 'info')) // 刘德华:40岁 // 访问对象中没有的属性,返回undefined console.log(Reflect.get(obj, 'height')) // undefined // receiver参数能够改变get属性访问器中的this指向 const objNew = { name: '张学友', age: 38 } console.log(Reflect.get(obj, 'info', objNew)) // 张学友:38岁
- obj.prop
-
set()
- obj.prop = value
// Reflect.set(target, prop, value, receiver) const obj = { name: '刘德华', set info(value){ this.name = value } } console.log(Reflect.set(obj, 'info', '周润发')) // true console.log(obj.name) // 周润发 const objNew = { name: '张学友' } // receiver参数能改变set属性访问器中的this指向 if(Reflect.set(obj, 'info', '吴彦祖', objNew)){ console.log(obj.name) // 周润发 console.log(objNew.name) // 吴彦祖 }
- obj.prop = value
-
has()
- prop in obj
// Reflect.has(target, prop) const obj = { name: '刘德华', age: 40 } // 旧写法 console.log('name' in obj) // true console.log('gender' in obj) // false // 新写法 console.log(Reflect.has(obj, 'name')) // true console.log(Reflect.has(obj, 'gender')) // false
- prop in obj
-
deleteProperty()
- delete obj.prop
// Reflect.deleteProperty(target, prop) const obj = { name: '刘德华', age: 40 } // 旧写法 // delete obj.age // 新写法 // 属性能删除则返回true,不能删除则返回false,被删除的属性依然存在 console.log(Reflect.deleteProperty(obj, 'age')) // true console.log(obj) // { name: '刘德华' } Reflect.defineProperty(obj, 'gender', { value: '男', configurable: false, // 不可配置,所以该属性无法被删除 enumerable: true, writable: true }) console.log(obj) // { name: '刘德华', gender: '男' } console.log(Reflect.deleteProperty(obj, 'gender')) // false console.log(obj) // { name: '刘德华', gender: '男' }
- delete obj.prop
-
其它方法对照
// 新写法 // Reflect.defineProperty(target, prop, descriptor) // Reflect.getPrototypeOf(target) // Reflect.setPrototypeOf(target, prototype) // Reflect.isExtensible(target) // Reflect.preventExtensions(target) // Reflect.ownKeys(target) // 旧写法 // Object.defineProperty(target, prop, descriptor) // Object.getPrototypeOf(target) // Object.setPrototypeOf(target, prototype) // Object.isExtensible(target) // Object.preventExtensions(target) // Object.getOwnPropertyNames(target) + Object.getOwnPropertySymbols(target)
六、Proxy对象
-
简介
- Proxy 对象是ES6新出的一个特性,用于创建一个对象的代理,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
- Object.defineProperty只能监听对象的属性,不能监听数组,而Proxy监听的是对象,包括数组
- 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
var proxy = new Proxy(target, handler); /* target: 表示所要代理的目标对象 handler:是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。 */
-
handler中的13种拦截方法
/* has(target, prop): Reflect.has(target, prop)方法的捕捉器。 get(target, prop, receiver): Reflect.get(target, prop, receiver)方法的捕捉器。 set(target, prop, value, receiver): Reflect.set(target, prop, value, receiver)方法的捕捉器。 deleteProperty(target, prop): Reflect.deleteProperty(target, prop)方法的捕捉器。 apply(target, thisArg, argArray): Reflect.apply(target, thisArg, argArray)方法的捕捉器。 construct(target, argArray, newTarget): Reflect.construct(target, argArray, newTarget)方法的捕捉器。 ownKeys(target): Reflect.ownKeys(target)方法的捕捉器。 getPrototypeOf(target): Object.getPrototypeOf(target)方法的捕捉器。 setPrototypeOf(target, prototype): Object.setPrototypeOf(target, prototype)方法的捕捉器。 isExtensible(target): Object.isExtensible(target)方法的捕捉器。 preventExtensions(target): Object.preventExtensions(target)方法的捕捉器。 getOwnPropertyDescriptor(target, prop): Object.getOwnPropertyDescriptor(target, prop)方法的捕捉器。 defineProperty(target, prop, descriptor): Object.defineProperty(target, prop, descriptor)方法的捕捉器。 */
-
示例
-
set()和get()方法
const person = { name: '周杰伦', phone: '18888888888' } let personHandler = { // 拦截属性的获取 get(target, prop, receiver){ // console.log(receiver) // console.log(`正在获取属性:${prop}`) if(prop === 'phone'){ return '经纪人电话: 13066666666' } return Reflect.get(target, prop, receiver) }, // 拦截属性的设置 set(target, prop, value, receiver){ // console.log(`正在设置属性:${prop}`) if(prop === 'gender'){ console.log('无法设置gender属性') }else{ return Reflect.set(target, prop, value, receiver) } } } // 创建代理对象,第一个参数是被代理的对象,第二个参数是处理对象 const proxy = new Proxy(person, personHandler) // 通过代理获取对象的属性 console.log(proxy.name) // 周杰伦 console.log(proxy.phone) // 经纪人电话: 13066666666 // 通过代理设置对象的属性 proxy.gender = '男' proxy.age = 30 // gender属性无法设置 console.log(person.gender) // undefined console.log(person.age) // 30
-
apply():拦截函数对象调用
// 原函数 function sum(a, b){ return a + b } //拦截sum函数调用 let proxy_sum = new Proxy(sum,{ apply(target, thisArg, argArray) { console.log('参数:' + argArray) return Reflect.apply(target, thisArg, argArray) } }) console.log(proxy_sum(10, 20)); // 30
-
construct():拦截new操作
/* function Person(name,age){ this.name = name; this.age = age; this.say = function (){ console.log('姓名:'+this.name+' 年龄:'+this.age) } } */ class Person{ constructor(name,age) { this.name = name this.age = age } say(){ console.log('姓名:'+this.name+' 年龄:'+this.age) } } let proxy_person = new Proxy(Person, { construct(target, argArray, newTarget) { console.log('初始化参数:' + argArray) return Reflect.construct(target, argArray, newTarget) } }) let p = new proxy_person('eliwang', 20) p.say()
-
七、Promise对象
-
简介
- Promise就是JS中专门用来存储异步代码的对象,可以确保异步代码的执行和返回结果
-
创建
- 创建Promise对象,需要指定一个回调函数作为参数,然后将需要执行的代码编写到该回调函数中
- 回调函数参数
- resolve:在代码正常执行时,来设置返回值
- reject(可选):在代码执行出错时,来设置错误信息
- 回调函数参数
- 创建Promise对象,需要指定一个回调函数作为参数,然后将需要执行的代码编写到该回调函数中
-
执行
- 顺利执行时,会执行then()方法,并将resolve返回的结果作为参数,传递给then()方法内的回调函数
- 执行异常时,会执行catch()方法,并将错误信息传递给内部的回调函数
// 创建Promise对象,正常执行,返回'顺利执行' const promise1 = new Promise((resolve, reject) => { setTimeout(() =>{ resolve('顺利执行') console.log('异步代码') // 会先执行该句代码,再返回执行结果'顺利执行' }, 2000) }) // 创建Promise对象,返回报错信息'执行异常' const promise2 = new Promise((resolve, reject) => { setTimeout(() =>{ reject('执行异常') console.log('异步代码') }, 2000) }) // 当Promise对象中的代码顺利执行时,会执行then()方法,并将resolve返回的结果作为参数,传递给then()方法内的回调函数 promise1 .then(result => console.log(result)) .catch(e => console.log(e)) // 当Promise对象中的代码返回错误信息时,会调用catch()方法,并将错误信息作为参数,传递给catch()方法内的回调函数 promise2 .then(result => console.log(result)) .catch(e => console.log(e))
-
then的链式调用
- 如果上一个then()中的返回值不是Promise对象,则将该返回值直接传递给下一个then()
- 如果上一个then()中的返回值是Promise对象,则将Promise对象中resolve返回值传给下一个then()
// 定义3个函数,分别返回promise对象 function fn1(n){ return new Promise(resolve => { setTimeout( () => resolve(n + 1), 1000 ) }) } function fn2(n){ return new Promise(resolve => { setTimeout( () => resolve(n + 2), 1000 ) }) } function fn3(n){ return new Promise(resolve => { setTimeout( () => resolve(n + 3), 1000 ) }) } // 如果上一个then()的返回值不是Promise对象,则将该返回值传给下一个then // 因此下面执行结果是:30 fn1(10) .then(n => 20) .then(n => 30) .then(n => console.log(n)) .catch(e => console.log(e)) // 如果上一个then()的返回值是Promise对象,则将上一个Promise对象中resolve返回值传给下一个then // 因此下面执行结果是:10+1+2+3 = 16 fn1(10) .then(n => fn2(n)) .then(n => fn3(n)) .then(n => console.log(n)) .catch(e => console.log(e))
-
异步调试
-
原则
- 能跳过的则跳过(异步前后,目标数据是否均为加密/解密的,只要一致就可以跳过)
- 如果不能跳过,遇到下面这种情形的
- n = n.then(t.shift(), t.shift())
- 首先定位到该代码处,不要下断点
- 向上跟栈,找到一个可以确定的地方先下断点
- 再在此处下上断点
- 断住之后,找参数数组的第一个方法,该方法大概率是加密/解密地方
- n = n.then(t.shift(), t.shift())
-