知识点:
let和const
暂时性死区
let和const命令声明的变量无变量提升。如果区块中存在let和const命令,这个区块对这些命令声明的变量就形成封闭的作用域。在声明之前使用这些变量,就会报错,这就是暂时性死区。
顶层对象
var和function的全局声明会自动绑定到window或global对象,这时es5全局变量的一个缺陷,let和const不会。
const命令
const声明的变量只是引用无法修改,对象的内部结构可以改变,使用object.Freeze()可以彻底锁死某对象,需递归锁定多层级对象。
变量的解构赋值
一种更优雅的赋值写法。
按照一定的模式,从数组或对象中提取值,对变量进行赋值的过程。
数组的结构赋值
let arr = [0,1,2];
let [a,b,c] = arr;
console.log(a);//0
console.log(b);//1
console.log(c);//2
对象的解构赋值
对象的解构赋值和数组的解构赋值其实类似,但是数组的数组成员是有序的,而对象的属性则是无序的,所以对象的解构赋值简单理解是等号的左边和右边的结构相同。
let {name,age} = {name:'swr',age:28};
console.log(name);//swr
console.log(age);//28
注意,对象的解构赋值是根据key值进行匹配的。
函数参数
首先解构赋值允许指定默认值,这为函数参数设置默认值提供基础。
各种数据结构的扩展
字符串
``表示模板字符串。
箭头函数
箭头函数,this的指向在函数定义时就定义好了。原因:箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。
双冒号运算符
代替bind,call,apply绑定this对象指向。Foo::bar(arguments)相当于bar.apply(foo,arguments)
尾调用
某个函数的最后一步再调用另一个函数。
就是最后返回值为执行另一个参数return anotherFunction(),而return anotherFunction()+1不属于尾调用,因为在执行完anotherFunction后还需要+1。尾调用的优势在return后,可以释放当前函数执行所需要的一切资源空间。
例:
function a(x){
return b(x);
}
尾递归
在一个尾调用中,如果函数最后的尾调用位置上是这个函数本身,则被称为尾递归。
对象
Object.assign:合并对象,把多个对象合并到第一个对象上。
Object.create:以某原型,生成一个新对象。可选第二个参数,为属性描述符。
Object.getPrototypeOf、Object.setPrototypeOf:获取和设置对象的原型属性proto。
Object.getOwnPropertyDescriptors:获取对象的属性信息,包括value,writable,enumerable,configurable。
ES6允许字面量定义对象时,用表达式作为属性名,把表达式放在方括号内。
const propKey = 'foo';
const obj = {
[propKey]: true,
['a' + 'bc']: 123
};
obj // { foo: true, abc: 123 }
Promise
Promise是异步编程的解决方案,是es6的原生对象。有三种状态:pending-进行中,resolved-已成功,reject-已失败。
宏任务与微任务
js的执行机制是事件循环机制(Event Loop),js有同步任务和异步任务,同步任务在主线程中排队执行,异步任务在事件队列中等待执行,主线程中的同步任务执行完毕时,系统才会读取事件队列中的异步任务执行。在es6中,又新增了宏任务与微任务的概念。
所有任务分为宏任务(macrotask )和微任务(microtask ) 两种。
MacroTask(宏任务):* script
全部代码、setTimeout
、setInterval
、setImmediate
、I/O
、UI Rendering
。
MicroTask(微任务):* Process.nextTick(Node独有)
、Promise
、MutationObserver
。
在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在宏任务的队列中取出第一个任务,执行完毕后取出微任务队列中的所有任务顺序执行;之后再取宏任务,周而复始,直至两个队列的任务都取完。
题目:
题目1:
var tmp = 123;
if(true){
tmp = 'abc';
let tmp;
}
tmp='abc'能否定义成功?
Answer:不能,会抛出错误,let将tmp变量绑定在{}代码块之内,外部的tmp声明无效,tem='abc'处在死区,所以报错。
题目2:
typeof undeclared_variable;
if(true){
typeof tmp;
let tmp;
console.log(tmp);
}
typeof能否运行成功?console输出什么?
Answer:第一个typeof的值是undefined,第二个typeof抛出错误,console输出undefined。
题目3:
var a = 1;
console.log(Window.a);
let b = 1;
console.log(Window.b);
分别输出什么?
Answer:1,Undefined
题目4:
const定义的对象属性是否可以改变?
Answer:当const定义的是值类型(普通类型)变量时,其值确实不能修改;但当const定义的是引用类型(数组或对象)变量时,其属性值是可变的,因为const实际上保证的,并不是变量的值不变,而是变量指向的那个内存地址所保存的数据不得改动。对于值类型的数据,值就保存在变量指向的那个内存地址中,所以此时const定义的就是常量,不可以改变。但对于引用类型来说,变量指向的那个内存地址中存放的是指向实际数据的指针,const只能保证这个指针是固定的,而不能保证它指向的数据也是不变的。
如果想让对象的属性值不可改变,需要使用object.freeze()锁死:
let cc {'name':'eu','age':12};
object.freeze(cc);
cc.age = 15;
console.log(cc.age);
输出:12
题目5:
let {newName:nm,oldName:om} = {oldName:'小王',newName:'老王'}
console.log(nm);
console.log(om);
分别输出什么?
Answer:老王,小王
题目6:
let {newName:nm='小李',oldName:om} = {oldName:'小王'}
console.log(nm);
console.log(om);
分别输出什么?
Answer:小李,小王
题目7:
let {
newName:nm='小李',
oldName:om;midName:'中间人'
} = {
oldName:'小王',
newName:null,
midName:undefined
}
console.log(nm);
console.log(om);
console.log(mn);
输出什么?
Answer:null,小王,中间人
题目8:
let tree = {
root: {
leaf: {
left: 5,
right:5
}
}
}
let {root:{leaf:{left}}} = tree;
console.log(left);
输出?
Answer:5
相当于{left} = {left:5,right:5}
题目9:
var a = 1;
var b = 3;
let [a,b] = [b,a];
console.log(a);
console.log(b);
输出结果是?
Answer:3,1
相当于[a,b] = [3,1],交换两个变量的值可以用到这段代码。
题目10:
var [a,...b] = [1,2,3];
console.log(a);
console.log(b);
输出什么?
Answer:1,[2,3]
解构一个数组时,可以使用剩余模式,将数组剩余部份赋值给一个变量。
题目11:
用尾递归实现阶乘。
Answer:
function fact(n,r){
if(n < 0){
return 1*r;
}else{
return fact(n-1,r*n);
}
}
计算过程:
第一轮:fact(6,1)
第二轮:fact(5,6)
第三轮:fact(4,30)
第四轮:fact(3,120)
第五轮:fact(2,360)
第六轮:fact(1,720)
返回:720
题目12:
通过object.defineproperty的set和get实现一个数据的绑定。例如输入框输入的值即时显示在p标签内部。
Answer:
<html>
<head></head>
<body>
<input type="text" id="input">
<p id="p"></p>
</body>
<script>
var obj = {};
Object.defineProperty(obj,"name",{
get:function(){
return name;
},
set:function(value){
document.getElementById("input").value = value;
document.getElementById("p").innerHTML = value;
}
});
var input = document.getElementById("input");
input.addEventListener("input",function(event){
var text = event.target.value;
obj.name = text;
});
</script>
</body>
</html>
题目13:
实现函数的链式调用 a().b().c()。当然也可以b().c().a()
Answer:
function a() {
console.log('a')
this.b = function () {
console.log('b')
return this
}
this.c = function () {
console.log('c')
return this
}
return this
}
a().b().c()
题目14:
使用Promise封装的ajax。
Answer:
const getJSON = function(url) {
const promise = new Promise((resolve, reject) => {
const client = new XMLHttpRequest();
client.open("GET", url, false);//false-异步
client.onreadystatechange = function() {
//函数异步执行,实现每次变化都监听readyState的状态变化
if (this.readyState == 4) {//请求完成
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
}
client.responseType = "json";
client.setRequestHeader("Accept", "application/json");
client.send();//请求参数
});
return promise;
};
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.log('出错了'+ error);
});
题目15
红灯3秒亮一次,绿灯1秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?
Answer:
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
var light = function (timmer, cb) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
cb();
resolve();
}, timmer);
});
};
var step = function () {
Promise.resolve().then(function () {
return light(3000, red);
}).then(function () {
return light(2000, green);
}).then(function () {
return light(1000, yellow);
}).then(function () {
step();
});
}
step();
题目16:
以下代码输出什么?
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);
}, 0)
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);
Answer: 3,7,4,1,2,5
考察:js事件循环机制、宏任务与微任务。
题目17:
promise.all与promise.race的区别。
Anwser:promise.all接收一个函数数组,返回值为promise对象,函数并行执行,如果执行成功,在then中接收一个返回值数组;如果执行失败,返回最快失败的函数的reject。promise.race与promise.all相似,不同的是,执行成功后,它在then中接收最快执行完毕的函数的返回值。
题目18:
封装一个异步加载图片的方法。
Answer:
function loadImageAsync(url) {
return new Promise(function(resolve,reject) {
var image = new Image();
image.onload = function() {
resolve(image)
};
image.onerror = function() {
reject(new Error('Could not load image at' + url));
};
image.src = url;
});
}
题目19:
箭头函数作用域?箭头函数与function的区别在哪?
Answer:箭头函数的作用域指向定义变量时所在的作用域。箭头函数与function的主要区别在于this绑定的时间。Function的this,是在执行的时候进行绑定;箭头函数的this,是在声明的时候进行绑定,使用箭头函数可以避免指向不明确的问题。
网友评论