你可能不知道的元属性new.target

一道面试题

有一个构造函数Class,且不定义全局变量, 需满足如下条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 function Class(name){
// 待实现
}




满足:

const Class1 = new Class("张三");
const Class2 = new Class("李四");
Class1.id==1; // true
Class2.id==2; // true

console.log(Class1, Class2);
console.log(Class1.prototype == Class2.prototype); // true

const Class3 = Class("王五");
Class3.id==3; // true
console.log(Class3.prototype == Class2.prototype); // true
console.log(Class3.prototype == Class1.prototype); // true

instanceof 问题

在es5中,通常判断一个对象是否是一个类的实例的时候使用instanceof关键字,例如如下代码:

1
2
3
4
5
6
7
function Person(name) {
if (this instanceof Person) {
this.name = name
} else {
throw new Error('必须使用new关键字来调用Person')
}
}

当判断this不是Person类的实例的时候,将会抛出一个异常。

但是如下通过call或apply修改this绑定作用域,那么 在Person类中是无法区分到底是通过Person.call()或Person.apply()还是new关键字调用得到的Person实例

所以,如下代码也能够正常执行:

1
2
let person = new Person('Leevare')
let anotherPerson = Person.call(person, 'Jason') //this.name='Jason', 对person对象name赋值

image

在es6中引入了new.target这个元属性。当通过new的方式调用时,new.target将会被赋予新创建的对象实例,此处则为Person,其它方式将会为undefined。

1
2
3
4
5
6
7
function Person(name) {
if(new.target !== Person) {
throw new Error('必须使用new关键字来调用Person')
}else {
this.name = name
}
}

此时,若使用call或apply则会抛出异常。
image

new.target

使用场景: 如果一个构造函数不通过 new 命令生成实例, 就报错提醒或自身返回new 实例

ES6引入了new.target属性,用于确认当前作用的在哪个构造函数上。若没有通过new命令调用构造函数。
则new.target会返回undefined,否则返回当前构造函数。

new.target语法由一个关键字”new”,一个点,和一个属性名”target”组成。通常”new.”的作用是提供属性访问的上下文,但这里”new.”其实不是一个真正的对象。不过在构造方法调用中, new.target指向被new调用的构造函数,所以”new.”成为了一个虚拟上下文

new.target这个属性,当子类继承父类会返回子类的构造函数名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Parent {
constructor() {
// Child子类继承父类, 那么父类构造函数中的 new.target 是子类构造函数的名称
console.log(new.target)
}
}

class Child extends Parent {
constructor() {
super()
}
}

// Child

new.target属性适用于所有函数访问的元属性。

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
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>

        function Class(name{
            if (new.target == Class) {
                this.name = name;
                this.id = ++Class.id;
            } else {
                return new Class(name);
            }
        }
        Class.id = 0;
        Class.prototype = {
            say() {

            }
        }

        const Class1 = new Class("张三");

        const Class2 = new Class("李四");


        console.log(Class1, Class2);

        console.log(Class1.prototype == Class2.prototype);

        const Class3 = Class("王五");
        console.log(Class3);


        console.log(Class3.prototype == Class2.prototype);
        console.log(Class3.prototype == Class1.prototype);

    </script>
</body>

</html>

image

或者

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<script>

function Class(name) {
if (this instanceof Class) {
this.name = name;
this.id = ++Class.id;
} else {
return new Class(name);
}
}
Class.id = 0;
Class.prototype = {
say() {

}
}

const Class1 = new Class("张三");
const Class2 = new Class("李四");


console.log(Class1, Class2);
console.log(Class1.prototype == Class2.prototype);

const Class3 = Class("王五");
console.log(Class3);


console.log(Class3.prototype == Class2.prototype);
console.log(Class3.prototype == Class1.prototype);

</script>
</body>

</html>

image

Promise输出顺序面试题

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
const timeout = ms => new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, ms);
});

const ajax1 = () => timeout(5000).then(() => {
console.log('1');
return 1;
});

const ajax2 = () => timeout(1000).then(() => {
console.log('2');
return 2;
});

const ajax3 = () => timeout(2000).then(() => {
console.log('3');
return 3;
});

const mergePromise = ajaxArray => {
// 在这里实现你的代码
// 保存数组中的函数执行后的结果
var data = [];

// Promise.resolve方法调用时不带参数,直接返回一个resolved状态的 Promise 对象。
var sequence = Promise.resolve();

ajaxArray.forEach(function (item) {

console.log(item);

// 第一次的 then 方法用来执行数组中的每个函数,
// 第二次的 then 方法接受数组中的函数执行后返回的结果,
// 并把结果添加到 data 中,然后把 data 返回。
sequence = sequence.then(item).then(function (res) {
data.push(res);
return data;
});
})

// 遍历结束后,返回一个 Promise,也就是 sequence, 他的 [[PromiseValue]] 值就是 data,
// 而 data(保存数组中的函数执行后的结果) 也会作为参数,传入下次调用的 then 方法中。
return sequence;

};

mergePromise([ajax1, ajax2, ajax3]).then(data => {
console.log('done');
console.log(data); // data 为 [1, 2, 3]
});

// 要求分别输出
// 1
// 2
// 3
// done
// [1, 2, 3]
</script>
</head>

<body>

</body>

</html>
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
53
54
55
56
57
58
59
60
61
62
63
64
65
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
const timeout = ms => new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, ms);
});

const ajax1 = () => timeout(5000).then(() => {
console.log('1');
return 1;
});

const ajax2 = () => timeout(1000).then(() => {
console.log('2');
return 2;
});

const ajax3 = () => timeout(2000).then(() => {
console.log('3');
return 3;
});

const mergePromise = async (urls) => {
const arr = [];

// 并发执行
const textPromises = urls.map(url => {
return url();
});

// 按次序输出
for (const textPromise of textPromises) {
arr.push(await textPromise);
}

return arr
};

mergePromise([ajax1, ajax2, ajax3]).then(data => {
console.log('done');
console.log(data); // data 为 [1, 2, 3]
});

// 要求分别输出
// 1
// 2
// 3
// done
// [1, 2, 3]
</script>
</head>

<body>

</body>

</html>

JS找出数组array出现最多的item项

不区分数组item类型

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<script>
var a = [1, 'a', 2, 3, 4, '4', 'c',
'aaa', 54, 54, 54, 54, 534, 43, 4,
56, 76, 8, 678, 78, 77, 97, 1, '54',
1, "1", "a"];
var b = a.reduce((obj, item) => {
item in obj ? obj[item]++ : obj[item] = 1
return obj
}, {});
var c = Object.entries(b).sort((a, b) => {
return b[1] - a[1]
});
console.log(c);
console.log(c[0]);
</script>
</body>

</html>

image

区分数组item类型

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>

<body>
<script>
var a = [1, 'a', 2, 3, 4, '4', 'c', 'aaa',
54, 54, 54, 54, 534, 43, 4, 56, 76, 8,
678, 78, 77, 97, 1, '54', 1, "1", "a"];
var b = a.reduce((map, item) => {
if (map.has(item)) {
const val = map.get(item);
map.set(item, val + 1);
} else {
map.set(item, 1);
}
return map;
}, new Map());
var c = [...b.entries()].sort((a, b) => {
return b[1] - a[1]
});
console.log(c);
console.log(c[0]);
</script>
</body>

</html>

image