1. let和var的区别:
题目:
for(var i=0;i<3;i++){
setTimeout(() => {
console.log(i);
}, 1);
}
for(let j=0;j<3;j++) {
setTimeout(() => {
console.log(j);
}, 1);
}
答案:
333
012
解析:
由于JavaScript中的事件执行机制,setTimeout函数真正被执行时,循环已经走完。 由于第一个循环中的变量i是使用var关键字声明的,因此该值是全局的。在循环期间,我们每次使用一元运算符++都会将i的值增加1。 因此在第一个例子中,当调用setTimeout函数时,i已经被赋值为3。在第二个循环中,使用let关键字声明变量i:使用let(和const)关键字声明的变量是具有块作用域的(块是{}之间的任何东西)。 在每次迭代期间,i将被创建为一个新值,并且每个值都会存在于循环内的块级作用域。
2. 运算符的优先级:
题目:
var a = {
n: 1
}
var b = a;
a.n = a = {
m: 2
}
console.log(a)
console.log('b', b)
答案:
{m: 2}
b {n: {m:2}}
解析:
假设{n:1}存在1000的地址,先把{n:1}给b,那么b也指向1000地址,然后a.n就是先拿出1000地址的n,然后进行a={M:2},这个时候a不指向1000了,然后把这个a给到刚刚从1000地址拿出来的n,所以这个时候1000地址的对象变成了{n:{m:2}}。b是指向1000地址的,所以b是{n:{m:2}}。而a是{n:1};
3.obj与Set
题目:
const obj = {
1: 'a',
2: 'b',
3: 'c'
};
const set = new Set([1, 2, 3, 4, 5]);
console.log(obj.hasOwnProperty('1'));
console.log(obj.hasOwnProperty(1));
console.log(set.has('1'));
console.log(set.has(1))
答案:
true true false true
解析:
所有对象键(不包括Symbols)都会被存储为字符串,即使你没有给定字符串类型的键。 这就是为什么obj.hasOwnProperty('1')也返回true。上面的说法不适用于Set
在Set中没有“1”:set.has('1')返回false。它有数字类型1,set.has(1)返回true。
4. promise执行顺序
题目:
setTimeout(() => {
console.log('tet1');
});
var p = new Promise((resolve, reject) => {
resolve();
});
setTimeout(() => {
console.log('tet2');
new Promise(function(resolve, reject) {
resolve();
}).then(()=>{
console.log('promise1');
})
});
setTimeout(() => {
console.log('set3');
});
p.then(function(){
console.log('promise');
});
console.log(1234);
答案:
解析:
promise是微任务,setTime是宏任务2, js执行会先执行微任务,微任务队列空了, 再去看宏任务//同步执行完,先去执行微,所以先promise,微任务队列空了,去执行宏,所以接着set1,set2,set2里又加入一个微任务,微任务队列有了,就先做微任务,所以在是promise1,最后在set3
5.obj中的this指向
题目:
const shape = {
radius: 10,
diameter() {
console.log(this.radius * 2);
},
per: () => {
console.log(2 * Math.PI * this.radius);
}
}
shape.diameter();
shape.per();
答案:
20
NaN
解析:
diameter 中this 指向shape, this.radius * 2 => 20
per 中,this指向 windows,2*Math.PI * this.radius => NaN
请注意,diameter是普通函数,而per是箭头函数。对于箭头函数,this关键字指向是它所在上下文(定义时的位置)的环境,与普通函数不同! 这意味着当我们调用perimeter时,它不是指向shape对象,而是指其定义时的环境(window)。没有值radius属性,返回undefined。
6.立即执行函数
题目:
var foo = {
bar: function() {
return this.baz;
},
baz:1
};
(function(){
console.log(typeof arguments[0]());
return typeof arguments[0]()
})(foo.bar)
答案:
undefined
解析:
在arguments()中执行了一个方法,arguments[0]就是foo.bar方法,
arguments[0]()相当于arguments.0()(伪代码),
注意:这在foo.bar中的this是没有绑定到foo。
虽然 foo.bar 传递给了函数,但是真正执行的时候,
函数 bar 的上下文环境是 arguments,并不是 foo。
所以在执行this.baz的时候自然返回的就是undefined,typeof调用的话就转换成”undefined”了。
7.
题目:
var foo = {
bar: function() {
return this.baz;
},
baz: 1
}
f = foo.bar;
console.log(f());
console.log(f.call(foo));
答案:
undefined, 1
解析:
把foo.bar存储给f然后调用,
所以this在foo.bar引用的是全局对象,所以就没有baz属性了。
换句话说,foo.bar执行的时候上下文是 foo,
但是当 把 foo.bar 赋值给 f 的时候,
f 的上下文环境是 window ,是没有 baz 的,所以是 ”undefined”。
通过call使函数中的this指向 foo,因此为foo.baz
8. this在函数中的调用
题目:
var length = 10;
function fn() {
console.log(this);
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn){
console.log(this);// this => obj
fn(); // this=> window
arguments[0](); // this => Arguments
}
}
obj.method(fn,1);
答案:
10,2
解析:
第一次输出10应该没有什么异议,这里的this指向window,
第二个调用arguments[0]()相当于执行arguments调用方法,this指向arguments,而这里传了两个参数,故输出arguments长度为2。
9.变量提升
题目:
var c = 1;
function c(c) {
console.log(c);
var c = 3;
}
c(2);
答案:
报错
解析:
首先声明函数c,再声明变量c时发现有同名属性,被忽略,接下来执行赋值语句 c=1,此时赋值成功,c成功转为一个变量。
当函数和变量都会被提升时,函数先被提升,然后才是变量。在处理变量声明时,若发现
作用域已有同名属性则忽略;但函数声明时会覆盖同名属性
var c = 1;
function fn(c) {
console.log(c); // 2 形参相当于一个局部变量,已经定义且赋值
var c = 3;
}
fn(2);
10.变量提升
题目:
var foo = {
n: 1
};
function fun(foo) {
var foo;
console.log(foo.n); // 由于foo为对象引用,因此输入原来的值
foo.n = 3; // 将全局的 foo.n的值修改
foo = { // 局部新建关系,之前的引用断开
n: 2
};
console.log(foo.n); // 2
};
fun(foo);
console.log(foo.n); // 3
11.不同数据类型间的加法
题目:
var a = 10;
a.pro = 10;
console.log(a); // 10
console.log(a.pro); // undefined
console.log(a.pro + a); //NaN
var s = 'hello';
s.pro = 'world';
console.log(s); // hello
console.log(s.pro); // undefined
console.log(s.pro + s); // undefinedhello
12.函数参数的个数
题目:
function test(x,y,z) {
console.log(test.length); // 3
console.log(arguments.length); //2
console.log(arguments.callee === test); // true
console.log(arguments[2]) // undefined
// arguments.callee 指向arguments对象所在的函数
}
test(10,20)
解析:
function.length : 获取形参的个数
arguments.length: 获取实参的个数
13.reduce的用法
题目:
console.log([[0,1],[2,3]].reduce((acc,cur) => {
return acc.concat(cur);
})) // [0,1,2,3]
答案:
[1, 2, 0, 1, 2, 3]
解析:
其实就是考察reduce用法,
[1,2]是初始值。 这是我们开始执行reduce函数的初始值,以及第一个acc的值。
在第一轮中,acc是[1,2],cur是[0,1]。 我们将它们连接起来,结果是[1,2,0,1]。
然后,acc的值为[1,2,0,1],cur的值为[2,3]。
我们将它们连接起来,得到[1,2,0,1,2,3]。
14.数组去深度,去重,排序
题目:
// 一直如下数组:
// 编写一个程序将数组扁平化并除去其中重复部分的函数,最终得到一个升序且不重复的数组
var arr = [[11,22,22],[13,14,15,15],[16,17,18,19,[11,12,[12,13,[14]]],12]];
答案:
解法一:利用了join函数
function sortArray(arr) {
let array = arr.join(',').split(',');
array = array.map(el => +el);
array = array.sort(function(x,y){
if(x>y) return 1;
if(x<y) return -1;
return 0;
})
let result = [];
for(let i = 0;i<array.length;i++){
result.indexOf(array[i]) === -1 && result.push(array[i]);
}
return result;
}
console.log(sortArray(arr));
解法二:
var arr = [
[11,22,22],
[13,14,15,15],
[16,17,18,19,[11,12,[12,13,[14]]],12]
];
function sortArray(arr) {
let result = [];
arr = mergeArray(arr);
if(!arr.length) {
return arr;
}
arr.map(el => {
result.indexOf(el) === -1 && result.push(el);
});
result = result.sort((x,y) =>{
x>y && return 1;
x<y && return -1;
return 0;
})
return result;
}
function mergeArray(arr) {
let result = [];
let needMerge = false;
if(!arr.length) {
return arr;
}else {
arr.map(el => {
(toString.call(el) === '[object Array]') ? result.push(...el) : result.push(el);
})
}
result.map(el => {
if(toString.call(el) === '[object Array]') {
needMerge = true;
}
})
if(needMerge) {
return mergeArray(result);
} else {
return result;
}
}
console.log(sortArray(arr))
答案三:
var arr = [
[11,22,22],
[13,14,15,15],
[16,17,18,19,[11,12,[12,13,[14]]],12]
];
// flat 方法接受一个参数n,可以将深度为n的数组扁平化,然后去重并排序,去重有set,排序用sort
// 当然也可以用递归等其他方法
var newArray = Array.from(new Set(arr.flat(Infinity))).sort((a,b) => a-b);
console.log(newArray);
// arr3.flat(Infinity) 使用 Infinity 作为深度,展开任意深度的嵌套数组
// Array.from(new Set(arr)) // 去重
15.通过函数实现基本数据类型的复制
题目:
// 实现一个函数clone,可以对JavaScript中的5中主要的数据类型
//(包括Number, String, Object, Array, Boolean) 进行值复制
答案:
function clone(obj) {
// 非复杂类型,直接拷贝
if(obj === null || obj === undefined || typeof obj !== 'object') {
return obj;
}
var con = obj.constructor;
// data和正则类型比较特殊
if(con === Date) {return new Date(obj)};
if(con === RegExp) {return new RegExp(obj)};
// 区分Array
var o = con === Array ? [] : {};
for(var e in obj) {
o[e] = typeof obj[e] === 'object' ? obj[e].clone() : obj[e];
}
return o;
}
16. 修改对象原型
题目:
String.prototype.giveLydiaPizza = () => {
return 'give Lydia pizza!'
}
const name = 'Tom';
console.log(name.giveLydiaPizza());
答案:
give Lydia pizza!
解析:
String是一个内置的构造函数,我们可以为它添加属性。
我刚给它的原型添加了一个方法。
原始类型的字符串自动转换为字符串对象,由字符串原型函数生成。
因此,所有字符串(字符串对象)都可以访问该方法!
当使用基本类型的字符串调用giveLydiaPizza时,实际上发生了下面的过程:
1、创建一个String的包装类型实例
2、在实例上调用substring方法
3、销毁实例
17.
题目:
var a = ?;
if(a == 1&&a==2&&a==3) {
console.log('hello');
}
// 什么条件 输出'hello'
答案:
var a;
a = {
index:1,
valueOf:function() {
return this.index++;
}
}
if(a == 1&&a==2&&a==3) {
console.log('hello');
}
18.报错执行顺序
题目:
(() => {
let x,y;
try{
throw new Error();
} catch (x) {
(x =1),(y=2);
console.log(x); // 1
}
console.log(x); // undefined
console.log(y); // 2
})();
答案:
1,undefined,2
解析:
try 里面抛异常,直接进入catch,catch的回调参数为x,但又在catch的作用域里面将x赋值为1,y赋值为2,所以第一个console的值是1,第三个console的值是2;因为在catch的作用域内只对catch的回调参数x进行赋值,与上层定义的x无关,所以第二个console的值为undefined
19.new 方法,与非new 方法的区别
题目:
function Person(firstName,lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const lydia = new Person('Lydia', 'Hallie');
const sarah = Person('Sarah', 'Smith');
console.log(lydia);
console.log(sarah);
console.log(this.firstName);
console.log(this.lastName)
答案:
Person {firstName: "Lydia", lastName: "Hallie"}
undefined
Sarah
Smith
解析:
对于sarah,我们没有使用new关键字。 使用new时,它指的是我们创建的新空对象。 但是,如果你不添加new它指的是全局对象!我们指定了this.firstName等于'Sarah和this.lastName等于Smith。 我们实际做的是定义global.firstName ='Sarah'和global.lastName ='Smith。 sarah本身的返回值是undefined因此this.firstName和this.lastName分别是Sarah和Smith了
20.作用域,原型链
题目:
function Foo() {
getName = function () {
console.log(1);
};
console.log(this)
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();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
答案: