new操作符

3/21/2022 js

# new操作符

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

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

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

function A() {
    return [123]
}
new A() // [123]
1
2
3
4

# 实现一个new

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

# instanceof

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

# 实现一个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)
    }
}
1
2
3
4
5
6
7
8
9

# 实现私有变量

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

class Person {
    constructor(age) {
        this._age = age
    }
}
let p = new Person()
// 还是可以获取p._age
1
2
3
4
5
6
7

比较好的方法是结合闭包 + 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]
})()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 浅拷贝

// 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()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 深拷贝

// 一:只能用于对象内部没有方法时
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

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

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())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 继承

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

Animal.prototype.eat = function (food) {
    console.log(this.name + "正在吃" + food)
}
1
2
3
4
5
6
7
8

# 构造继承

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

var cat = new Cat()
1
2
3
4
5

# 原型链继承

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

}

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

var cat = new Cat()
1
2
3
4
5
6
7
8
9

# 组合继承

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

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

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

var cat = new Cat()
1
2
3
4
5
6
7
8
9

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

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

Cat.prototype = Object.create(Animal.prototype)
Cat.prototype.constructor = Cat
1
2
3
4
5
6

# es6的extends

class Cat extends Animal {
    constructor(name) {
        super(name)
    }
}
1
2
3
4
5
Last Updated: 9/7/2022, 11:32:53 PM