跳到主要内容

Object

new操作符

  1. 首先创建一个空的对象,空对象的__proto__属性指向构造函数的原型对象

    const obj = {
    __proto__: fn.prototype
    }
  2. 把上面创建的空对象赋值构造函数内部的this,用构造函数内部的方法修改空对象

  3. 如果构造函数返回一个非基本类型的值a,则返回这个值a,否则返回上面创建的对象obj

function A() {
return [123]
}
new A() // [123]

实现一个new

function _new(fn, ...arg) {
const obj = Object.create(fn.prototype);
const ret = fn.apply(obj, arg);
return ret instanceof Object ? ret : obj;
}

instanceof

({}) instanceof Object; // true
[] instanceof Array; // true
[] instanceof Object; // true

实现一个instanceof

function myInstanceof(a, b) {
if (typeof a !== 'object' || a === null) return false
let proto = Object.getPrototypeOf(a)
while(true) {
if (proto === null) return false
if (proto === b.prototype) return true
proto = Object.getPrototypeOf(proto)
}
}

实现私有变量

最简单的方式是提前约定好私有变量

class Person {
constructor(age) {
this._age = age
}
}
let p = new Person()
// 还是可以获取p._age

比较好的方法是结合闭包 + Symbol。

如题目:创建一个 Person 类,其包含公有属性 name 和私有属性 age 以及公有方法 setAge ;创建一个 Teacher 类,使其继承 Person ,并包含私有属性 studentCount 和私有方法 setStudentCount 。

// 这里写在一个立即执行函数里,分开写也是可以的
const [Person, Teacher] = (function () {
const _age = Symbol('age')
const _studentCount = Symbol('studentCount')
const _setStudentCount = Symbol('setStudentCount')
class Person {
constructor(name, age) {
this.name = name
this[_age] = age
}

setAge(age) {
this[_age] = age
}
}

class Teacher extends Person {
constructor(name, age, count) {
super(name, age)
this[_studentCount] = count
}
[_setStudentCount](count) {
this[_studentCount] = count
}
set(count) {
this[_setStudentCount](count)
}
}
return [Person, Teacher]
})()

浅拷贝

// Object.assign
let source = {
name: 'akara',
age: 20,
}
let target = Object.assign({}, source)

// 扩展运算符
let source = {
name: 'akara',
age: 20,
}
let target = {...source}

// slice
let source = [1, 2, 3]
let target = source.slice()

// concat
let source = [1, 2, 3]
let target = source.concat()

深拷贝

// 一:只能用于对象内部没有方法时
JSON.parse(JSON.stringify(obj))

// 二: 递归,简陋版本
// 属性值可以是数组或对象,此时进行递归
// 属性值也可以函数
function deepClone(source) {
let target = null
if (typeof source === 'object' && source !== null) {
target = Array.isArray(source) ? [] : {}
for (let [key, value] of Object.entries(source)) {
target[key] = deepClone(value)
}
} else {
target = source
}
return target
}

// 但无法解决循环引用的问题
// 例如
let obj = {}
obj.a = obj
deepClone(obj)
// 会一直递归执行deepClone,造成函数栈溢出


// 复杂版本
// 使用WeakMap解决循环引用的问题
// 使用WeakMap而不是Map是因为其使用的弱引用。该引用不会被垃圾回收器记录。
function deepClone(source, hash = new WeakMap()) {
let target
if (hash.has(source)) {
return hash.get(source)
}
if (typeof source === 'object' && source !== null) {
target = Array.isArray(source) ? [] : {}
hash.set(source, target)
for (let [key, value] of Object.entries(source)) {
target[key] = deepClone(value, hash)
}
}
else {
target = source
}

return target
}
var obj = {}
obj.a = obj
deepClone(obj)

不过以上的深克隆只克隆了对象自身的属性,丢失了原型链上的属性,为了不丢失,可以这么做

function completeDeepClone(source) {
function deepClone(source, hash = new WeakMap()) {
// ... 上面的代码
}
let ret = deepClone(source)
Object.setPrototypeOf(ret, Object.getPrototypeOf(source))
return ret
}

// 使用
function Animal(name) {
this.name = name
}
Animal.prototype.master = 'akara'
completeDeepClone(new Animal())

继承

function Animal(name, size) {
this.name = name
this.size = size
}

Animal.prototype.eat = function (food) {
console.log(this.name + "正在吃" + food)
}

构造继承

  1. 可以多继承。
  2. 只能继承父类的实例属性和方法,不能继承原型属性和方法
function Cat() {
Animal.call(this)
}

var cat = new Cat()

原型链继承

  1. 不能多继承。
  2. 所有新实例会共享父类的属性
// cat >= Cat.prototype >= Animal.prototype >= Object.prototype
function Cat() {

}

Cat.prototype = new Animal()
Cat.prototype.name = "cat"

var cat = new Cat()

组合继承

可以继承实例属性和方法,也可以继承原型属性和方法 缺点: 调用两次父类构造函数

function Cat (name) {
Animal.call(this)
this.name = name
}

Cat.prototype = new Animal()
Cat.prototype.constructor = Cat

var cat = new Cat()

寄生组合继承(除es6继承外最推荐的方法)

function Cat(name) {
Animal.call(this)
}

Cat.prototype = Object.create(Animal.prototype)
Cat.prototype.constructor = Cat

es6的extends

class Cat extends Animal {
constructor(name) {
super(name)
}
}