es5中不常用的api总结和实现

126 阅读5分钟

Object.preventExtensions(对象)

阻止给一个对象添加新属性

const obj = {
    name: 1
}
Object.preventExtensions(obj)
obj.age = 2
obj // { name: 1 }

Object.seal(对象)

创建一个封闭对象,这个对象只能修改,不能删除,添加和配置

 const obj = {
     a: 1
 }
 Object.seal(obj)
 obj.b = 2
 delete obj.a
 obj //  { a: 1 }

手动模式实现Object.seal()

/*
通过Object.preventExtensions来限制对象添加属性,通过configurable:false限制删除
*/
function seal(obj){
    Object.preventExtensions(obj)
    for(const key in obj){
        Object.defineProperty(obj, key, {
            writable: true,
            configurable: false, // 不可配置
        })
    }
}

Object.freeze(对象)

冻结一个对象,不能删除,新增和修改,这个对应的属性如果是其他对象,那么不受影响

const obj = {
    a: 2,
    b: { c: 3 }
}
Object.freeze(obj)
obj.a = 4
obj.b.c = 5
obj // { a: 2, b: { c: 5 }}

模拟实现Object.freeze()

/*
    内部通过Object.seal来限制对象的属性添加,删除和配置,
    再通过修改每个属性的writable为false来限制修改属性
*/
function freeze(obj){
    Object.seal(obj)
    for(const key in obj) {
        Object.defineProperty(obj, key, {
            writable: false
        })
    }
}
const obj2 = { a:1 }
freeze(obj2)
obj2.a = 2
obj2.b =3
obj2 // { a:1 }

key in obj

检查指定的属性是否在这个对象身上包括它的原型链

const obj = {
    a: 1
}
const obj2 = Object.create(obj)
'a' in obj2 // true

obj.hasOwnProperty(key)

检查指定属性是否在这个对象身份,不会检查它的原型链

const obj = {
    a: 1
}
const obj2 = Object.create(obj)
obj2.hasOwnProperty('a') // false

for in

遍历数组或对象身上的可枚举的属性,包括它的原型链上的可枚举属性;for in一般用来遍历对象,数组使用for循环

const arrObj = { a: 'a'}
const arr = [1,2,3]
Object.setPrototypeOf(arr, arrObj)
for (const k in arr) {
    if (arr.hasOwnProperty(k)) {
        console.log(k) // 0 1 2 3
    }
    console.log(k) // 0 1 2 3 a
}

for of

只能遍历可迭代的对象身上的属性不管是否可枚举,但是不会遍历它原型上的属性,不可迭代的对象会报错,因为内部使用了迭代器进行循环,一般用来遍历数组可以直接获取到其中的值

const arrObj = [5,6]
const arr = [1,2,3]
Object.setPrototypeOf(arr, arrObj)
Object.defineProperty(arr, 3, {
    enumerable: false,  // 把下标3设置为不可枚举
    value:333
})
console.log(arr) // 1 2 3 333
for (const val of arr) {
    console.log(val) // 1 2 3 333
}

模拟实现一个for of

function forOf(arr){
    const iterator = arr[Symbol.iterator]()
    let result = iterator.next()
    while(!result.done){
        console.log(result.value)
        result = iterator.next()
    }
}
forOf([1,2,3]) // 1 2 3

Object.keys和Object.values

Object.key获取对象身上可枚举的属性名,不包括原型链;
Object.values获取对象身上可枚举的值,不包括原型链;

Object.getOwnPropertyNames()

获取对象身上自有的所有的属性名,包括可枚举和不可枚举,但是不包括原型链

const obj = {
    a: 1
}
const obj2 = Object.create(obj, { b: {
    enumerable: false
}})
const name = Object.getOwnPropertyNames(obj2)
name // ['b']

writable属性描述

如果在原型上添加的属性是不可修改的,那么在子对象上就无法通过.的形式创建这个属性,严格模式下会报错

const obj = {
    a: 1
}
Object.defineProperty(obj, 'b', { writable: false })
const c = Object.create(obj)
c.b = 2 // 严格模式下会报错
c.a = 3
c // { a: 3 }

解决: 只能通过Object.defineProperty来修改这个属性

set描述符

如果在原型对象的属性上使用了set描述符,那么在子对象身上是无法通过.进行创建这个属性,如果通过.创建了这个属性,那么它就会修改原型上的这个属性的值

const obj = {
    a: 1
}
let value = ''
Object.defineProperty(obj, 'b', { 
    set(val){ // 设置了set
        value = val
    },
    get(){
        return value
    }
})
const c = Object.create(obj)
c.b = 66 // 给c对象添加属性b值为66
c // {}
c.b // 66 从原型上获取
Object.getPrototype(c) // {a:1,b:66}

解决: 只能通过Object.defineProperty来修改这个属性,并且会修改了原型上的这个属性

Object.getOwnPropertyDescriptor(对象,属性)

获取对象上指定属性的描述符

const obj = {
    a: 1
}
Object.defineProperty(obj, 'b', { writable: false })
Object.getOwnPropertyDescriptor(obj, 'b') 
/*
configurable: false
enumerable: false
value: undefined
writable: false
*/

Object.getOwnPropertyDescriptors(对象)

获取对象上的所有属性的描述符

const obj = {
    a: 1
}
Object.defineProperty(obj, 'b', { writable: false })
Object.getOwnPropertyDescriptors(obj)
/*
结果
{
    a:{
       configurable: true
       enumerable: true
       value: 1
       writable: true
    },
    b:{
        configurable: false
        enumerable: false
        value: undefined
        writable: false
    }
}
*/

组合继承

// 实现一个父类
function Person(name){
    this.name = name
}
// 实现一个子类
function Man(){
    // 继承属性
    Person.apply(this, [...arguments])
}
// 通过原型继承方法
// 方式1:直接继承原型,缺点:如果修改了子类的原型属性,父类也会进行修改
Man.prototype = Person.prototype
// 方式1 的正确继承,通过创建对象的形式
Man.prototype = Object.create(Person.prototype)

// 方式2 通过父类的构造函数继承 缺点:父类中属性也会被子类继承,比如name,如果子类没有这个属性,那么就会找到父类中的name,还有一些副作用(写日志,修改状态等)会影响到子类
Man.prototype = new Person()
// 方式2 正确的写法,通过中介纯函数
function C(){}
C.prototype = Person.prototype
Man.prototype = new C()

//方式3 Object.setPrototypeOf
Object.setPrototypeOf(Man, Person.prototype)

// 修改构造函数的指向
Man.prototype.constructor = Man

instanceof

右侧函数的原型(函数名.prototype)是否在左侧对象的原型链上,它只能判断对象和函数之间的关系,无法判断两个对象之间的关系;
通过中间函数来判断两个对象之间的关系

/*
方法1
a为左侧对象,b为右侧对象
*/
function isInstanceof(a,b){
  function F(){}
  F.prototype = b
  // 不能使用setPrototypeof修改原型,因为setPrototypeof内部使用
  // __proto__进行修改,__proto__和prototype不是一个东西,F.__proto__指向
  //的是F的构造函数的原型也就是Function.prototype,
  // 而F.prototype就是F.prototype
  // Object.setPrototypeof(F,b)
  return a instanceof F
}
const obj = {}
const c = Object.create(obj)
isInstanceof(c,obj) // true

// 方法2 通过isPrototypeOf
c.prototype.isPrototypeOf(obj) // true

// 方法3 通过getPrototypeof
Object.getPrototypeof(c) === obj // true

// 方法4 通过__proto__
c.__proto__ === obj // true

setPrototypeOf和prototype和__proto__的区别?

  1. prototype只有构造函数才有这个属性,表示构造函数的原型,而对象不具有这个属性;
  2. __proto__对象和构造函数都有这个属性,对象的__proto__是指向它的构造函数的prototype,而构造函数䣌__proto__指向它的构造函数(Function)的prototype;
  3. 因此构造函数身上的__proto__和prototype是两个不同的东西
  4. Object.setPrototypeOf(a,b)给对象设置原型,内部通过__proto__进行设置的,相当于a._proto_ = b;
function A(){}
const a = new A()
a.__proto__ === A.prototype // true
A.__proto__ === Function.prototype // true
Object.setPrototypeOf(A, {})
// Object.setPrototypeOf(A, {})是修改了构造函数A的原型,而不是修改了A.prototype

模拟_proto_

Object.defineProperty(Object.prototype, '__proto__', {
  set(o){
    Object.setPrototypeOf( this, o )
    return o
  },
  get(){
    return  Object.getPrototypeOf( this )
  }
})

Object.create(obj,{属性:{描述符}})

创建一个新的对象,并且指定它的原型,并且可以添加新的属性和描述符

const obj = {
    a: 1,
}
const obj2 = Object.create(obj, {
    b: {
        enumerable: true,
    }
})
obj2 // { b: undefined }

模拟实现一个create

function create(obj, params){
    function F(){}
    F.prototype = obj
    const f = new F()
    if (params) {
        const keys = Object.keys(params)
        for (const k of keys) {
            Object.defineProperty(f, k, {
                ...keys[k]
            })
        }
    }
    return f
}
const obj = {
    a: 1,
}
const obj2 = create(obj, {
    b: {
        enumerable: true,
    }
})
obj2 // { b: undefined }