1.说出并解释下列代码的输出结果:
function Foo() {
getName = function () {
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
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
new new Foo().getName(); //3
解析
(1) . Foo.getName()
直接调用 Foo 的静态方法 ,Foo.getName被重新复制,所以打印2
(2). Foo().getName()
Foo() 就是普通函数,返回的this是window,后面调用window.getName(),而winsow下的getName在第一步调用的时候被重新复制,所以打印1
(3). getName()
调用window中的getName,由于window中的getName在(2)中执行的时候被重新赋值,所以打印1.
(如果将(3)放在(2)前面执行,打印结果就为4)
(4). new Foo.getName()
同(1)效果一样,直接调用 Foo 的静态方法 ,Foo.getName被重新复制,所以打印2
(5). new Foo().getName()
通过实例访问 Foo原型链上的方法,所以打印3
(6) . new new Foo().getName()
首先 new Foo() 得到一个空对象 {}
第二步向空对象中添加一个属性 getName,值为一个函数
第三步 new {}.getName()
等价于 var bar = new (new Foo().getName)(); console.log(bar) 先 new Foo 得到的实例对象上的 getName 方法,再将这个原型上 getName 方法当做构造函数继续 new ,所以执行原型上的方法,打印 3
2.写出打印结果,并分析出原因
var a = 10
var obj = {
a: 100,
pro: {
getPro: () => {
console.log(this.a)
},
},
}
obj.pro.getPro() //10
解析
getPro是一个箭头函数的方法,在箭头函数中不会创建自己的this,只会集成作用于链上一层的this,所以这里的this指向为window,打印结果为10
3.写出打印结果,并分析出原因
var a = { n: 1 }
var b = a
a.x = a = { n: 2 }
console.log(a.x) // undefined
console.log(b.x) // {n:2}
解析
首先,双等号的执行顺序是从左到右
那么a.x = a = {n:2} 就等价于 a.x = a ; a={n:2}
由于b = a 属于浅拷贝,那么再对a赋值时也会传递给b, 经过a.x处理过后b.x = {n:1},然后将{n:2}赋值给a同时会传递给b.x
此时的a={n:2} , b={n: 1,x: {n: 2}}
所以打印结果依次为 undefined , {n: 2}
4.写出打印结果,并分析出原因
var length = 10
function fn(){
console.log(this.length)
}
var obj = {
length : 5 ,
method :function (fn){
fn();
arguments[0]()
}
}
obj.method(fn,1)
解析
首先,我在全局定义了一个变量length,一个对象obj和一个函数fn . length赋值为10。
接下来是fn函数,输出this.length,对象obj中obj.length是5,obj.method是一个函数,这个函数里面调用了fn函数,arguments是一个伪数组,代表method函数的实参
method函数当中调用的fn函数是全局当中的函数,所以this指向的是window,this.length就是10,arguments0代表的就是调用arguments里面的第一项,也就是传参进来的fn,所以这个this指向的就是arguments,arguments.length的值为2
所以最后的打印结果为10 ,2
5.写出打印结果,并分析出原因
function a(xx){
this.x = xx
return this
}
var x = a(5)
var y = a(6)
console.log(x.x)
console.log(y.x)
解析
首先,我们在全局定义了一个变量x,一个变量y,和一个函数a,函数a当中的this.x等于接受到的参数,返回this,.这里要注意,返回的是this,不是this.x 接下来我们给X赋值 值为a(5) ,又给y赋值,值为a(6).最后我们输出x.x 和y.x
分析完代码的定义, 我们来看输出结果, a(5) 执行的时候传递一个参数为5,调用函数a, 返回this 此时的this指向window, 所以 x = window , 但是后面又执行了y=a(6), 再次调用fn 将 this.x修改为6, 所以此时window中的 x 为6, 返回结果为window, 所以y=window,
由于 x =6 所以x.x = undefined, y=window, window中的x为6 , 所以y.x 为 6
打印结果依次为 undefined 和 6
6.阅读下面代码,写出打印结果的先后顺序
new Promise(function (resolve, reject) {
console.log('A')
resolve()
}) //A J B H K C I L D G E F
.then(function (resolve, reject) {
new Promise(function (resolve, reject) {
console.log('B')
resolve()
})
.then(function () {
console.log('c')
})
.then(function () {
new Promise(function (resolve, reject) {
console.log('D')
resolve()
})
.then(function () {
console.log('E')
})
.then(function () {
console.log('F')
})
console.log('G')
})
console.log('H')
})
.then(function () {
console.log('I')
})
new Promise(function (resolve, reject) {
console.log('J')
resolve()
})
.then(function () {
console.log('K')
})
.then(function () {
console.log('L')
})
打印结果
A J B H K C I L D G E F
解析:
注:下文中的轮次只是为了方便理解
首先,我们来看最外层的两个newPromise,里面同步语句会先执行,所以最先打印出来的是A和J
因为每个Promise都会产生一个微任务,所以到第一轮的微任务中Promise的第一个then会进入到第一轮的微任务中,下面我们来单独看这两个then. 第一个Promise的第一个then里面又new了一个新的Promise,这个新的Promise产生一个微任务,本轮的微任务已经在执行中了,所以这个微任务会排到下一个微任务队列的第一位,还是先执行里面的同步语句,打印B和H ,之后运行低二个Promise的第一个then,打印K
第一轮的微任务执行完毕,开始第二轮微任务,先执行第三个Promise的第一个then,打印C,继续执行第一个Promise的第二个then,打印I,最后执行第二个Promise的第二个then,打印K
第三轮微任务开始,执行第三个Promise的第二个then,这个then里面又new了一个新的Promise,同理,新的Promise(第四个)产生的微任务放入下一轮第一个执行,此时执行同步语句,打印D和G
第四轮微任务开始执行第四个Promise的第一个then,打印E
第五轮微任务开始执行第四个Promise的第二个then,打印F
综上,我们最后得到的结果就是:
A
J
B
H
K
C
I
L
D
G
E
F
7.阅读下面代码,写出他们的打印顺序
new Promise((resolve,reject)=>{
console.log('A')
setTimeout(() => {
console.log('B')
}, 0);
console.log('C')
resolve()
console.log('D')
}).then((resolve,reject)=>{
console.log('E')
new Promise((resolve,reject)=>{
console.log("F")
resolve()
console.log('G')
}).then(()=>{
setTimeout(()=>{
console.log('H')
})
console.log('I')
}).then(()=>{
console.log('J')
})
}).then(()=>{
console.log('K')
})
setTimeout(() => {
console.log('L')
}, 0);
new Promise((resolve,reject)=>{
console.log('M')
resolve()
}).then(()=>{
setTimeout(() => {
new Promise((resolve,reject)=>{
console.log('N')
resolve()
}).then(()=>{
setTimeout(()=>{
console.log('O')
},0)
}).then(()=>{
console.log('p')
})
}, 0);
})
console.log('Q')
打印结果
ACDMQEFGKJBLNPHO
解析
首先,我们要知道微任务会先于宏任务执行,知道了这一点,我们来看下面的代码.
现在,我们将微任务列表和宏任务列表分开梳理,最后再合并结果
先看微任务,第一层为Promise--promise--console.log,第一个Promise中的setTimeOut创建宏任务1,最外层的setTimeOut创建宏任务2
所以第一轮按从上到下顺序打印 A,C,D,M,Q
然后看第二轮微任务,分别为第一个Promise中的then,和第二个Promise中的then,由于第二个Promise中的then中执行setTimeOut,创建宏任务3,所以第二轮先打印 E,然后创建Promsie,打印F和G,目前的及结果为A,C,D,M,Q,E,F,G
然后看第三轮,由于第二个Promise中的then中创建了宏任务,所以第三轮微任务只在第一个promise中的第二个then中,从代码中可以看出,第一个Promise中的第一个then执行后,有两个then,前者创建宏任务4,打印I,后者打印K,所以目前打印顺序为A,C,D,M,Q,E,F,G,I,K
然后第四轮微任务只剩下第三轮中的第二个then所调用的then了,打印结果为J,至此,微任务已经执行完毕,结果顺序为:
A,C,D,M,Q,E,F,G,I,K,J
开始执行宏任务,第一轮按上方创建顺序为宏任务1,2,3,4
宏任务1打印B,宏任务2打印L,宏任务3 第一轮微任务打印N,然后第二轮创建宏任务5,第三轮打印P, 宏任务4打印H
第一轮宏任务 打印顺序为B,L,N,P,H
第二轮宏任务执行宏任务3创建的宏任务5,打印O
至此,宏任务执行完毕,结果为B,L,N,P,H
由于宏任务是在微任务执行完成后执行,所以需要将宏任务的打印结果连接在微任务后方,结果为
A
C
D
M
Q
E
F
G
I
K
J
B
L
N
P
H
O
8.力扣 - 柠檬水找零
在柠檬水摊上,每一杯柠檬水的售价为5美元
顾客排队购买你的产品,(按账单bills支付的顺序)一次购买一杯
每位顾客只买一杯柠檬水,然后向你付5美元、10美元或者20美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付5美元
注意,一开始你手头没有任何零钱
如果你能给每位顾客正确找零,返回true,否则返回false
function func(bills) {
let a = 0 //5元
let b = 0 //10元
//循环所有数据
for (let i = 0; i < bills.length; i++) {
//如果顾客给的是5元
if (bills[i] === 5) {
// 手中的5元加一
a += 1
} else if (bills[i] === 10) {
//顾客给的是10元
//手中的5元减一,10元加一
b += 1
a -= 1
} else {
//顾客给的是20元,有两种找零方式
// 找3个5元, 或者找1个10元1个5元
if (b >= 1) {
//10元足够的时候优先找一个10元一个5元
//10元减一,5元减一
a -= 1
b -= 1
} else {
//10元不够是找三个5元, 5元减3
a -= 3
}
}
//当5元也不够时说明没有零钱了,返回false
if (a < 0) {
return false
}
}
//循环完成,说明零钱足够,返回true
return true
}
let result = func([5, 5, 5, 10, 20])
console.log(result)