var a = []
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i)
}
}
a[6]() // 10
答:输出结果为10。因为var是全局变量,当执行a6的时候,函数体中的变量i,JS引擎会根据作用域去查到,在函数作用域里查不到i的定义,就会往上层作用域查找,直到在全局作用域中查到i,此时i已经变成了10。 如果希望调用ai的结果输出i,可以将for循环中的i的声明由var改为let。因为let会产生块级作用域。执行函数时函数内的变量i可以在for循环里面每次产生的块级作用域里查到i的值。
var a = []
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i)
}
}
a[6]() // 6
除此之外,还可以通过闭包的形式,用函数作用域摆脱全局作用域的影响。将每一轮的变量i放到闭包中,执行函数的时候,JS引擎根据作用域查找时,在当前闭包中就可以查到当前的i的值,而当前i的值就是每轮循环时的i。
var a = []
for (let i = 0; i < 10; i++) {
a[i] = (function(i){
return function () {
console.log(i)
}
})(i)
}
a[6]() // 6
var tmp = 123
if (true) {
console.log(tmp)
let tmp
}
答:会在console.log(tmp)时报错:ReferenceError: Cannot access 'tmp' before initialization 因为在ES6中,let定义的变量会产生块级作用域,虽然在全局用var tmp = 123 定义了tmp,但是在进入if里面后,又用let重新定义了tmp,此时的if作用域的tmp都是let变量声明的块级变量,全局变量tmp已经被屏蔽了。当执行console.log(tmp)时,根据作用域的查找原则,JS引擎会先在代码执行处的最内层的块级作用域中查找,如果查找不到才会往上级作用域查找。而在if块级作用域中有tmp的声明,但是console.log(tmp)在执行的时候,tmp还未被初始化,所以会报错。
var arr = [12, 34, 32, 89, 4]
const minValue = Math.min(...arr)
console.log(minValue) // 4
答:let const都是块级作用域,let是变量,const是常量。var定义的变量在创建和初始化的过程会提升。let和const定义的变量创建过程会提升,但初始化过程没有提升。而const只有创建和初始化过程,没有赋值过程,不可以被重新赋值。
console.log(t) // undefined
var t
console.log(t) // ReferenceError: Cannot access 't' before initialization
let t
var a = 10
var obj = {
a: 20,
fn() {
setTimeout(() => {
console.log(this.a)
})
}
}
obj.fn()
答:输出20。setTimeout中的函数是箭头函数,箭头函数中的this指向的是上层作用域中的this,也就是普通函数fn(){}函数体的this。而fn()是对象obj的成员方法,在调用时fn内的this指向函数的调用者,也就是obj,所以this.a就是obj.a,所以输出20。
答:Symbol是ES6新增的一种基础数据类型,最主要的作用就是为对象添加独一无二的属性名。console.log(Symbol() === Symbol()) // false
const obj = {
[Symbol()]: 11
}
此时obj的这个Symbol属性不可被枚举。
答:JS的数据类型分为值类型和引用类型。值类型变量是存放在栈中,引用类型变量是将内存地址放在栈中,而栈中的地址指向了堆中的一块区域,堆中这个地址存放的内容才是这个引用类型变量的真正的内容。 在浅拷贝时,值类型会直接拷贝,而引用类型只拷贝了变量的地址,新的变量和原变量还会指向同一个堆中的区域。新变量的改变会对原变量产生影响,因为内存地址相同,其中一个变量的改变,其实改变的是同一个内存中的东西。
let a = 1
let b = a
b = 2
// 值类型拷贝新变量不会对原变量产生影响。
console.log(a === b, a, b) // false 1 2
let c = {
name: 'cathy'
}
let d = c
d.name = 'jal'
// 引用类型拷贝新变量会对原变量产生影响。
console.log(c === d, c, d) // true { name: 'jal' } { name: 'jal' }
而在深拷贝时,遇到值类型会直接拷贝,遇到引用类型时,会进行递归拷贝,如果这个引用类型是数组/对象,则判断数组的每一个元素/属性值,如果是值类型,则直接拷贝,如果是引用类型,则再对这个元素/属性值进行判断,递归下去,直到拷贝完整个对象为止。 这是我之前写的深拷贝案例:
/**
* 深拷贝
* @param {Object} obj 要拷贝的对象
*/
function deepClone(obj) {
if (typeof obj !== 'object' || obj === null) return obj
let newObj
if (obj instanceof Array) {
newObj = []
} else {
newObj = {}
}
for (let key in obj) {
if (obj.hasOwnProperty(key))
newObj[key] = deepClone(obj[key])
}
return newObj
}
JS是单线程语言,指JS执行环境中那个负责执行代码的线程只有一个。但是JS的运行环境以及JS的某些API在运行时是可以开启新的线程的。当JS代码的回调栈中进行某些耗时任务时,会将这些耗时任务放到WebAPI中,等到可以执行的时候,就会进入消息队列中。当回调栈空的时候,会被Event Loop监听到,Event Loop会从消息队列中取出第一个任务放到回调栈中让主任务执行。 JS回调队列中的任务称之为【宏任务】,而宏任务执行过程中可以临时加上一些额外需求,可以选择作为一个新的宏任务进到队列中排队(如setTimeout),也可以作为当前任务的【微任务】,直接在当前任务结束后立即执行。
微任务的目的是为了提高整体的响应能力,目前绝大多数异步调用都是作为宏任务执行,Promise 、MutationObserver、process.nextTick 是作为微任务在本轮调用的末尾执行。
setTimeout(function() {
var a = 'hello'
setTimeout(function () {
var b = 'lagou'
setTimeout(function () {
var c = 'I love U'
console.log(a + b + c)
}, 10)
}, 10)
}, 10)
改为Promise后:
new Promise((resolve, reject) => {
setTimeout(() => {
var a = 'hello'
resolve(a)
}, 10);
})
.then(res=>{
return new Promise((resolve, reject) => {
setTimeout(() => {
var b = ' lagou'
resolve(res + b)
}, 10);
})
})
.then(res => {
return new Promise((resolve, reject) => {
setTimeout(() => {
var c = ' I love U'
resolve(res + c)
}, 10);
})
})
.then(res => {
console.log(res)
})
.catch(err=>{
console.log(err)
})
答:TypeScript是JavaScript的超集,是基于JavaScript之上的编程语言,它解决了JavaScript类型系统的问题。TypeScript的语法上对JavaScript兼容,JavaScript代码完全可以当做TypeScript代码运行。不过TypeScript在JavaScript基础上,有了更严格的类型要求。如果有错误的类型问题,JavaScript在运行时才会暴露出来,而TypeScript则会在项目编译初期就会报错。
答: 优点:
缺点:
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。