JS查漏补缺

判断相等、对象、函数、原型与继承

判断相等

一些不按套路出牌的例子

抽象相等

1
2
null == undefined
// -> true

严格相等

1
2
3
4
5
6
null === undefined
// -> false
NaN === NaN
// -> false
+0 === -0
// -> true

Object.is

1
2
Object.is(+0,-0) // -> false
Object.is(NaN,NaN) // -> true

对象

创建对象

字面量

使用对象字面量可实现简单的单例模式

1
2
3
4
var stu = {
name: 'yrq',
age: 25
}

Object.create

1
Object.create(obj)

以obj为原型创建对象。

当obj为null或{}时会创建一个空对象,不同的是null不继承任何对象,而使用{}创建的对象会具有__proto__属性。

1
2
3
4
5
6
7
8
9
10
var male = {
sex: 'male'
}
var yrq = Object.create(male)
yrq.name = "yrq"
console.log(yrq)
var rizu = Object.create(null, { name: { value: 'rizu'}, sex: { value: 'female'}})
console.log(rizu)

对象属性

属性的键

对象属性的键只能是字符串或symbol类型的对象。

属性描述

  • enumerable - 属性是否可枚举
  • configurable - 属性是否可删除
  • writable - 属性值是否可修改

添加属性

  1. 通过.属性直接添加

    1
    2
    3
    4
    5
    6
    var a = {
    foo: 'hi'
    }
    a.name = 'yrq'
    console.log(a)
    // -> {foo: "hi", name: "yrq"}
  2. Object.defineProperty
    使用defineProperty静态方法添加,注意属性描述的设置。

    1
    Object.defineProperty(obj, prop, descriptor)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var a = {
    foo: 'hi'
    }
    a = Object.defineProperty(a, {
    name: {
    value: 'yrq',
    enumerable: true,
    configurable: true,
    writable: true
    }
    })
    console.log(a)

遍历对象属性

假设有如下对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var stu = {
school: 'bistu'
}
var yrq = Object.create(stu, {
name: {
value: 'yrq',
enumerable: true,
configurable: true,
writable: true
},
age: {
value : 25,
enumerable: false,
configurable: true,
writable: true
}
}
)
console.log(yrq)
  1. 遍历本身和原型中的可枚举属性
    使用for…in语句

    1
    2
    3
    4
    for (let key in yrq) {
    console.log(key)
    }
    // -> name school
  2. 仅遍历本身的可枚举属性
    使用Object.keys静态方法

    1
    2
    3
    4
    for (let key of Object.keys(yrq)) {
    console.log(key)
    }
    // -> name
  3. 遍历本身的可枚举与不可枚举属性
    使用Object.getOwnPropertyNames静态方法

    1
    2
    3
    4
    for (let key of Object.getOwnPropertyNames(yrq)) {
    console.log(key)
    }
    // -> name age

限制对象

有三个静态方法可修改对象中属性。

  • Object.preventExtensions
    使对象无法添加新属性
  • Object.seal
    使对象无法修改属性值
  • Object.freeze
    使对象无法添加、修改与删除属性,并且无法修改属性描述

对象拷贝

浅拷贝

使用如下方法拷贝对面时实际拷贝的是指向对象的指针,通过修改拷贝后的变量属性会影响到原始的对象属性。

将拷贝后的变量设为null时不会影响栈中所存放对象的值。

1
2
3
4
5
6
7
8
var a = {
foo: 'hello'
}
var b = a
b.foo = 'hi'
console.log(a)
b = null
console.log(a)

深拷贝

  1. 若对象的属性中无函数类型,则可使用json序列化与解析的方法进行深拷贝。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var a = {
    number: 1
    }
    function deepCopyJSON(obj) {
    return JSON.parse(JSON.stringify(obj))
    }
    var b = deepCopyJSON(a)
    b.number = 2
    console.log(a.number)
  2. 使用递归遍历属性的方式进行对象深拷贝,适合所有情况。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var a = {
    number: 1,
    foo: function () {
    console.log('hi')
    }
    }
    function deepCopyEnmu (obj) {
    var newOBJ = {}
    if(typeof obj != 'object'){
    return obj
    }
    for (var arrt in obj) {
    newOBJ[arrt] = deepCopyEnmu(obj[arrt])
    }
    return newOBJ
    }
    var b = deepCopyEnmu(a)
    b.number = 2
    b.foo()
    console.log(a.number)

函数

函数声明

1
2
3
function foo () {
console.log('Hello')
}

使用函数声明式时会发生函数提升,在声明前调用会执行。

一个函数提升的例子:

1
2
3
4
5
6
7
function b(c) {
console.log(c);
function c() {
console.log('d');
}
}
b(10) // -> 会输出什么?

函数表达式

1
2
3
var foo = function () {
console.log('Hello')
}

匿名函数

匿名函数会立即执行。

1
2
3
(function(){
console.log('Hi')
})()

闭包

用JS高级编程第三版的话说,“闭包是一个有权使用另一个函数作用域中变量的函数”。

这样的函数多数情况下是作为函数的返回值,如下:

1
2
3
4
5
6
7
function add(x) {
return function result (y) {
return x + y
}
}
var a = add(10)
a(5)

原型与继承

  • 函数具有prototype属性,对象具有proto属性

使用函数与原型的继承

1
2
3
4
5
6
7
function Student () {
var school = 'bistu'
}
Student.prototype.age = 99
console.log(Student.prototype)
var yrq = new Student()
console.log(yrq.age)

使用Class类的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class People {
constructor () {
this.age = 99
}
}
class Student extends People {
constructor (name) {
super()
this.school = 'bistu'
this.name = name
}
}
var yrq = new Student('yrq')
console.log(yrq.age)
console.log(yrq.name)

获得对象的原型

proto属性

1
console.log(yrq.__proto__) // -> {age: 99}

Object.getPrototypeOf()

1
console.log(Object.getPrototypeOf(yrq)) // -> {age: 99}

多重继承

使用Object.assign融合多个原型对象即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var sex = {
sex: 'female'
}
var grade = {
grade: 3
}
function Student () {
var school = 'bistu'
}
Student.prototype = Object.assign({}, sex, grade)
var yrq = new Student()
console.log(yrq.__proto__) // -> {sex: "female", grade: 3}

文章目录
  1. 1. 判断相等
    1. 1.1. 抽象相等
    2. 1.2. 严格相等
    3. 1.3. Object.is
  2. 2. 对象
    1. 2.1. 创建对象
      1. 2.1.1. 字面量
      2. 2.1.2. Object.create
    2. 2.2. 对象属性
      1. 2.2.1. 属性的键
      2. 2.2.2. 属性描述
      3. 2.2.3. 添加属性
      4. 2.2.4. 遍历对象属性
    3. 2.3. 限制对象
    4. 2.4. 对象拷贝
      1. 2.4.1. 浅拷贝
      2. 2.4.2. 深拷贝
  3. 3. 函数
    1. 3.1. 函数声明
    2. 3.2. 函数表达式
    3. 3.3. 匿名函数
    4. 3.4. 闭包
  4. 4. 原型与继承
    1. 4.1. 使用函数与原型的继承
    2. 4.2. 使用Class类的继承
    3. 4.3. 获得对象的原型
      1. 4.3.1. proto属性
      2. 4.3.2. Object.getPrototypeOf()
    4. 4.4. 多重继承
|