练习1

const a = 888;
const b = new Number(888); // Number {888}
const c = '888';
console.log(a == b); // true
console.log(b == c); // true
console.log(a === b); // false
console.log(b === c); // false
1
2
3
4
5
6
7
  • ==在进行比较的时候会先进行类型转换,b是对象,首先会调用toString方法转为字符串'888',因此b == c为true;
  • a == b在进行比较时,b先转为字符串,然后再转为数字888,所以a == b也是true
  • ===在进行比较时不会进行类型转换,因此后两个都是false

练习2

const a = {};
const b = {name: 'lisi'};
const c = {name: 'wangwu'};
// 对象键值会被转为字符串
a[b] = 666;
a[c] = 888;

console.log(a[b]); // 888
console.log(a); // {[object Object]: 888}
1
2
3
4
5
6
7
8
9

练习3

const arr = [1, 2, 3];
arr[10] = 123;
console.log(arr); // (11) [1, 2, 3, empty × 7, 123]
1
2
3

练习4

核心知识点:箭头函数this指向。

const obj1 = {
    name: 'lisi',
    print() {
        return () => console.log(this.name);
    }
};

const obj2 = {name: 'wangwu'};
obj1.print()(); // lisi
obj1.print().call(obj2); // lisi
obj1.print.call(obj2)(); // wangwu
1
2
3
4
5
6
7
8
9
10
11

练习5

const obj = {
    1: 'a',
    2: 'b'
};
const set = new Set([1, 2, 3]);
// hasOwnProperty的参数要检测的属性的String字符串形式表示的名称,或者Symbol
// 如果参数不是字符串,将会自动转为字符串形式
console.log(obj.hasOwnProperty('1')); // true
console.log(obj.hasOwnProperty(1)); // true
console.log(set.has('1')); // false
console.log(set.has(1)); // true
1
2
3
4
5
6
7
8
9
10
11

练习6

async function async1() {
    console.log('async1 start'); // 2
    await async2();
    console.log('async1 end');
}

async function async2() {
    console.log('async2');
}

console.log('script start'); // 1

setTimeout(function() {
    console.log('setTimeout0');
}, 0);

async1();

new Promise((resolve) => {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('promise2');
});

console.log('script end');
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

输出

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout0
1
2
3
4
5
6
7
8
async function async1() {
    console.log('async1 start'); // 2
    await async2();
    console.log('async1 end');
}

async function async2() {
    console.log('async2');
}

console.log('script start'); // 1

setTimeout(function() {
    console.log('setTimeout0');
}, 0);

setTimeout(function() {
    console.log('setTimeout3');
}, 3);

setImmediate(() => console.log('setImmediate'));

process.nextTick(() => console.log('nextTick'));

async1();

new Promise((resolve) => {
    console.log('promise1');
    resolve();
    console.log('promise2');
}).then(() => {
    console.log('promise3');
});

console.log('script end');
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
script start
async1 start
async2
promise1
promise2
script end
nextTick
async1 end
promise3
setTimeout0
setImmediate
setTimeout3
1
2
3
4
5
6
7
8
9
10
11
12

练习7

为什么[] == ![]的结果为true? 判断步骤如下:

  1. 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值-->false转换为0,true转换为1。
  2. 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值。
  3. 如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,如果得到的值不是基本类型值,则基于返回值再调用toString方法(这个过程即ToPrimitive),用得到的基本类型值按照前面的规则进行比较。
  4. 如果两个操作数都是对象,则比较他们是不是同一个对象。如果两个操作数指向同一个对象,则相等操作符返回true, 否则返回false。

这两个操作符在进行比较时则要遵循下列规则:

  1. null和undefined是相等的。
  2. 要比较相等性之前,不能将null和undefined转换成其他任何值。
  3. 如果有一个操作数是NaN,则相等操作符返回false, 而不相等操作符则返回true, NaN != NaN。

练习8

var a = 0, b = 0;
function A(a) {
    A = function (b) {
        alert(a + b++);
    }
    alert(a++);
}
A(1); // 1
A(2); // 4
1
2
3
4
5
6
7
8
9

练习9

function Foo() {
    // 会覆盖全局的getName方法
    getName = function() {
        // console.log('----', this);
        console.log(1);
    }
    return this;
}
Foo.getName = function() { // 相当于静态方法
    console.log(2);
}
Foo.prototype.getName = function() {
    console.log(3);
}
var getName = function() {
    console.log(4);
}
// 会被覆盖
function getName() {
    console.log(5);
}

Foo.getName(); // 2
getName(); // 4
// Foo()当做普通函数调用,this指向window
Foo().getName(); // 1
getName(); // 1

// 涉及js运算符优先级,参考mdn
new Foo.getName(); // 2

// Foo()当做构造函数调用,this指向当前实例对象,相当于调用Foo原型上的getName
new Foo().getName(); // 3
new new Foo().getName(); // 3
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

new Foo.getName(); // 2
1

上述代码,因为成员访问优先级是19,new (无参数列表)优先级是18,所以先进行成员访问,即Foo.getName,Foo.getName指向如下函数:

Foo.getName = function() {
    console.log(2);
}
1
2
3

然后采用new调用该函数,因此输出2。

new Foo().getName(); // 3
1

上述代码,new (带参数列表)和成员访问优先级都是19,因此从左往右依次执行。Foo()当做构造函数调用,this指向当前实例对象,相当于调用Foo原型上的getName,因此输出3。

new new Foo().getName(); // 3
1

先执行new Foo(),返回Foo的实例对象,new Foo().getName指向的是Foo原型上的getName方法,相当于用new调用该方法,因此输出3 运算符优先级

'b' + 'a' + + 'a' + 'a'

('b' + 'a' + + 'a' + 'a') // "baNaNa"
('b' + 'a' + + 'a' + 'a').toLowerCase() // "banana"
+ 'a' // NaN
1
2
3

+ 'a'会进行位运算。

Function.prototype.a = 'a';
Object.prototype.b = 'b';
function Person(){};
var p = new Person();
// 因为p是对象,会找到对象原型上,而不会找到函数原型上
console.log('p.a: '+ p.a); // p.a: undefined
console.log('p.b: '+ p.b); // p.b: b
1
2
3
4
5
6
7