Q1.什么是ES6
ES6是ECMAScript的第六代版本
此前ECMAScript的历史
1996 ES1.0 (Netscape公司将js交给了ECMA组织 ES1.0正式面世)
1998 ES2.0
1999 ES3.0
2007 ES4.0(因为过于激进的改动和更新导致开发者不认同4.0版本,因此废除4.0版本)
2008 ES3.1(对ES4.0经过一年的完善和压缩,形成了现在使用最多的一个版本ES3.1(别称ECHarmony))
2009 ES5.0(在面世时就已经拟定的ES6.0的草案)
2013 ES6.0(ES6.0的草案完成)
2013.12 ES6.0(ES6.0的草案发布)
2015.06 ES6.0(ES6.0的正式发布)ES6如此繁琐的开发过程是为了避免ES4.0版本激进开发的错误
2016 ES7(从16年开始就不再使用旧版本号,而是以年份代号)
2017 ES8
2018 ES9
Q2.ECMAScript与JavaScript的关系
ECMAScript是一个编程语言的标准
JavaScript是实现这个编程语言的标准
JavaScript是由ECMAScript+WEBapi(DOM、BOM)组成的
flash是由actionScript+WEBapi(DOM、BOM)组成的 (已淘汰)
Q3.怎样学习ES6
将ES3.1写法与现在ES6写法相对比学习,思考有什么不同,会有什么样的优化
提供工具:Babel
Q4.ES6有什么用
新增了许多语法规范,可以对写法和开发有很大的帮助
但对技术上的提升较小
Q5.浏览器有几条线程
浏览器有5条线程
1.js线程(主线程)负责去执行栈顶代码
2.GUI线程
3.事件监听线程(负责监听各种事件)
4.计时线程(负责定时器)
5.网络线程(各种网路请求)
ES6语法
let
声明变量
ES3.1声明变量方式 //var num = 100
(直接挂再window上。容易造成全局污染)
ES6声明变量方式 //letnum = 100
(不会造成全局污染)
let不允许重复声明
let不会有声明提前 ?
在人为效果上是没有声明提前,而实则将数据保存到临时性死区中
Cannot access 'num' before initialization
//出现这种错误时表式将数据保存到临时性死区
{}
块级作用域
ES3.1局部作用域
(function(){
var num = 100;//通过匿名函数创建局部作用域
})()
ES6块级作用域
{
var num = 100;//与局部zuo'yong
}
const
常量声明
const不允许重复声明
const不会有声明提前
且常量不允许改变值 ?
//实则是不允许改变内存空间的地址。
const obj={};
obj.name = "jack"
console.log(obj)//{name:jack}
//const声明和赋值必须一次性完成,并且后期不允许改变存储空间地址
const abc;
abc = 123;
console.log(abc)//报错,
字符串
字符串的换行
ES3.1 //var str = “asd\n\asasa”
ES6 //let str = `asdasasa`
字符串的拼接(可以添加转义字符,可以嵌套)
ES3.1 //conslog.log(str + date + "asdsd")
ES6 //conslog.log(`${变量/表达式}asdasad`)
解构
结构化赋值,可以简写书写长度,提升效率
//对象的解构
let obj = {
name : "jack",
age : 18,
sex : "nan",
class : 10
}
let {name ,age,sex,class} = obj;
console.log(name ,age,sex,class);
//数组的解构
let arr=[1,2,3,4]
let[q,w,e,r] = arr;
console.log(q,w,e,r);
//数组的解构获取长度
let{length}= arr;
console.log(length);
数组赋值
//(ES3.1)
var a = arr[1]
var b = arr[0]
var c = arr[4]
var d = arr[3]
//(ES6)
let {1:a,0:b,4:c,3:d} = arr; //2,1,10,4
…运算符
1.收集符只能出现一次
2.收集符只能在参数的最后一个形参
收集功能
//(ES3.1)
function sum(){
console.log(arguments.push(6)) //报错,arguments是一个类数组,不能添加
}
sum(1,2,3,4,5)
//(ES6)
function sum(a,b...arg){ //收集参数只能放在最后一个参数
arg.push(6)//添加
console.log(a,b,arg)
}
sum(1,2,3,4,5)
展开
ES6(数组),
// 拼接
let arr = [1,2,3];
console.log(...arr);
let arr2 = [4,5,6];
let newArr = [...arr,...arr2];
console.log(newArr)
//克隆
let arr1 = [1,2,3,4,{name:"jack"}];
let arr2 = [...arr1];
console.log(arr1 == arr2);//false这是两个不同的数组
console.log(arr1[4] == arr2[4]); //true 只克隆了值而没有克隆地址称为浅克隆
ES7(object)
let company = {
name : "bailiban",
age : 16
};
let leader = {
name : "peng",
age : 18
}
let teachDepartment = {
leader : {
...leader
},
personNum : 20,
data : [new Date()]
};
函数
function fun(a,b,c){//当用户想输出a=0时必须进行处理
// a = 10;
// b = 20;
//ab为固定值// c是任意值 // 绝大数情况
// a = a || 10;
// b = b || 20;//输出为60
a = a === undefined && 10;//通过这样处理可以传递a = 0;
b = b === undefined && 20;
return a + b + c;
}
console.log( fun(0,0,30) ) //用户想传递a = 0;
ES6参数可以加默认值(new)
function fun(a,b=10,c=20){
return a+b+c;
console.log( fun(undefined,undefined,30) );//60
console.log( fun(1,2,3) )//6
console.log( fun(30) )//60,将默认值写在后面,可以直接传一个数
console.log( fun(10,undefined,30) )//50
console.log( fun(10,null,30) ) //40,null在数学中,默认的为0,不要传递null
}
ES6可以传递表达式(new)
× 不能传递语句 ×
//ES3.1创建一个元素 插入到某个元素中
// name 创建的标签 大多数为div
// container 需要往哪个元素中插入 大多数为container
// content 内容
function createElement(name,container,content){
const ele = document.createElement(name);
if(content){
ele.innerHTML = content
}
container.appendChild(ele)
}
createElement("div",document.getElementById("container"),"sdfsdfsd")
//成功,在页面中插入div内容为sdfsdfsd
//ES6创建一个元素 插入到某个元素中
//ES6可以在参数中传递表达式
function createElement(name = "div",container = document.getElementById("container"),content){
const ele = document.createElement(name);
if(content){
ele.innerHTML = content
}
container.appendChild(ele)
}
createElement(undefined,undefined,"sdfsdfsd")//成功,在页面中插入div内容为sdfsdfsd
函数>arguments
arguments在严格模式中arguments和函数的形参不存在关系,没有对应的映射关系
! 只要给函数加上了参数默认值,该函数会自动变成严格模式 !
!形参和let,const一样,有自己的作用域,根据声明的顺序会产生生临时性死区 !
//已自动开启严格模式
function test(a,b=10){
console.log("arguments",arguments[0],arguments[1])//arguments 1 2
console.log('a',a,"b",b)//a 1 b 2
a = 5;
console.log("arguments",arguments[0],arguments[1])//arguments 1 2
console.log('a',a,"b",b)//a 5 b 2
}
test(1,2)
函数>new.target(new)
//ES6以前
function Person(firstName,lastName){
if(!(this instanceof Person)){
throw new Error("该函数必须使用new调用")//这样就可以在直接调用下提示错误
//但这样是无法完全避免的
}
this.firstName = firstName;
this.lastName = lastName;
this.fullName = `${firstName} ${lastName}`
}
//面试点:new调用和直接调用结果
//new调用
const p1 = new Person("张","三")
console.log(p1)//成功调用
//直接调用
const p2 = Person("张","三")
console.log(p2)//失败undefined
//通过call改变指向依然会undefined
const p3 = Person.call(p1,"张","三")
console.log(p3)
//ES6
//使用new.target就可以真正避免此问题
if(new.target === undefined){//如果没有使用new 关键字调用函数,则返回undefined
throw new Error("该函数必须使用new调用")//如果使用了new调用,则返回的是函数的本身
}
函数>箭头函数
箭头函数是一个函数表达式,理论上任何使用函数表达式的地方都可以改成箭头函数
(参数1,参数2…)=>{函数体}
this指向问题
1.对象调用函数,this指向对象
2.直接调用函数,this指向window
3.如果使用了new关键字,this指向新创建的对象
4.如果使用apply,call,bind this指向绑定的数据
5.如果是DOM事件函数,this指向事件源
箭头函数语法
// 箭头函数的写法
// 简写
// 1.如果参数只有一个,可以省略小括号
// 参数 => {}
const print = num => {
console.log(num)
}
print(1000)
// 2.如果箭头函数只有一条返回语句,可以省略花括号,可以省略return
// 参数 => 返回值
const isOdd = function(num){
return num % 2 !== 0;
}
const isOdd = num => {
return num % 2 !== 0;
}
const isOdd = num => num % 2 !== 0;
console.log(isOdd(3))
console.log(isOdd(4))
// 2.1 如果返回值是一个对象的时候
const obj = (a,b) =>
({//只有花括号就会认为成函数体就会报错//把返回值变成表达式的形式就ok
a : a,
b : b
})
console.log( obj(1,2) )
//面试点
// 1.箭头函数中没有this,argument,new.target,如果要强行使用,则指向函数外层对应的this,argument,new.target
// 2.箭头函数没有原型,所有说占用空间非常小,不能当成构造函数来使用
console.log(func,func.prototype)
console.dir(func)
// 应用场景
// 1.临时使用的函数,并不会刻意的去调用
// 1.异步的处理函数
// 2.事件的处理
// 2.继续去沿用外层的this
// 3.对象的属性不要去用箭头函数,除非是特别的需求
// 4.数组方法的时候,保证代码的简洁
const arr = [1,2,89,45,123,54];
const result = arr.map(num => num*2);
console.log(result)
对象
1.成员速写
{ {
loginId:loginId, loginId,
loginPwd:loginPwd, ----> loginPwd,
nickName:nickName, nickName,
} }
//属性名和属性值相等的时候可以使用ES6成员速写
2.方法速写
3.计算属性名
const prop1 = "name"; //我们不知道属性名是什么,由后台返回给你的
const prop2 = "age";
const prop3 = "say"
const user = {
[prop1] : "尹老师",
[prop2] : 30,
[prop3](){
console.log(this[prop1],this[prop2])
}
}
console.log(user[prop1])
user[prop3]()
4对象的API
1.is()相等于===
语法:Object.is(值,值)//返回true/false
2.assign() 用于混合对象,带浅克隆 ...ES7
const obj1 = {
a : 123,
b : 456,
c : "abc"
}
const obj2 = {
a : 789,
e : "asdf"
}
const obj = Object.assign(obj1,obj2)// 将2的内容覆盖到1
console.log(obj == obj1)//true
3..getOwnPropertyNames() 枚举的顺序,返回一个数组,枚举出来对象的属性
// 先排数字,升序// 再排其他,按照书写顺序
const obj = {
e : 1,
a : 2,
c : 3,
0 : 6,
5 : 7,
2 : 3
}
console.log(obj)
const arr = Object.getOwnPropertyNames(obj);
4. setPrototypeOf() 设置某个对象的隐式原型 __proto__
const obj1 = {
a : 1
}
const obj2 = {
b : 2
}
// obj1.__proto__ = obj2 必须要通过构造函数来完成
Object.setPrototypeOf(obj1,obj2)
console.log(obj1)
构造函数
ES3.1缺点:属性和原型方法分开了,代码可读性差
ES6优点:从逻辑上说更合理,成为一个整体
ES6构造函数规范
1.类的声明不会被提升,和let const 一样,有临时性死区
2.类的所有代码全都是在严格模式中执行
3.类的所有方法都是不可枚举的
4.类的所有方法都无法当成构造函数直接使用
5.类的构造器必须使用new 来调用
对象中类的方法
1.可以写成计算属性名
2.可以使用getter 和 setter×暂未理解×
3.静态成员×暂未理解×
4.字段初始器(es7)
class Test{
a = 1; //简写,初始化的工作,如果没有使用static,就是实例成员
b = 2;
static c = 3
// constructor(){
// this.a = 1;
// this.b = 2
// }
const t = new Test()
console.log(t.a,t.b,t.c,Test.c)//1,2,ubdefined,3
- 类表达式
const A = class{ //匿名类,类表达式,类在js中本身就是表达式
a = 1;
b = 2;
}
const a = new A();
console.log(a)
//ES3.1构造函数
function Person(job,name,age,sex){
this.job = job;
this.name = name;
this.age = age;
this.sex = sex;
}
//增加实例方法
Person.prototype.print = function(){
console.log('工作:',this.job)
console.log('姓名:',this.name)
console.log('年龄:',this.age)
console.log('性别:',this.sex)
}
const a = new Person("程序猿","zhansan",30,"nan");
a.print()
//ES6构造函数
class Person{
constructor(job,name,age,sex){
this.job = job;
this.name = name;
this.age = age;
this.sex = sex;
}
print(){
console.log('工作:',this.job)
console.log('姓名:',this.name)
console.log('年龄:',this.age)
console.log('性别:',this.sex)
}
}
const a = new Person("程序猿","zhansan",30,"nan");
console.log(a)
a.print()
原型链(暂不理解)
符号
symbol(符号描述)来创建的
语法:const syb =symbol(参数为符号的描述);
作用:符号的设计,给对象去新增私有属性,只能在内部进行访问,外部无法使用
符号的特点
1.没有字面量的写法
2.symbol是在ES6中的一种数据类型(new)
3.每次调用symbol函数得到的符号永远不会相等,不管符号描述是否相同
4.符号可以作为对象的属性名使用,这种属性名叫符号属性
5.可以通过设计,让外面无法访问到
6.符号属性不能被枚举(针对符号特殊属性getOwnPropertySymbols(obj))
7.符号类型无法被隐式转换,数字运算,字符串拼接都是不行的
可以进行内部的显示转换
8.共享符号Symbol.for(“符号描述”) 如果符号描述相等,则可以得到同一个符号
const syb2 = Symbol.for("abc");
console.log(syb1,syb2)
console.log(syb1 === syb2)
const obj = {
a : 1,
b : 2,
[Symbol.for("c")] : 3
}
console.log(obj)
console.log(obj[Symbol.for("c")])
执行栈()
call stack 一个数据结构,存放各种函数执行环境每一个函数执行之前,它的相关信息会加入执行栈函数调用完成,会销毁执行栈
js引擎会永远执行栈顶。执行栈的最顶部
js是单线程。
面试题
i值问题
//要求点击对应的按钮弹出对应的数据
<button>0</button>
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>
<button>6</button>
<button>7</button>
<button>8</button>
<button>9</button>
<script>
//ES3.1(有且只有一种)(包裹一个函数产生作用域)
var buttons = document.getElementsByTagName("button");
for(var i = 0;i < buttons.length;i++){
(function(i){
buttons[i].onclick = function(){
console.log(i);
}
})(i)
}
//ES6
var buttons = document.getElementsByTagName("button");
for(let i = 0;i < buttons.length;i++){
buttons[i].onclick = function(){
console.log(i);
}
</script>
let解决闭包的问题
//控制台有什么内容,怎样改动才可以输出0123456789
var arr = [];
for(var i = 0;i<10;i++){
arr[i] = function(){
console.log(i);
}
}
arr[0]()
arr[1]()
arr[2]()
arr[3]()
arr[4]()
arr[5]()
arr[6]()
arr[7]()
arr[8]()
arr[9]()
console.log(i)
//实际上这个问题分成两问
//1.是输出数组0-9这个比较简单,在开始循环中将var i 改成 let i 即可
//2.输出这i这个才是个掉坑的地方,当你将开始循环中将var i 改成 let i之后{}形成了一个块级作用域,
//再想输出i是就会报错
//解决方法添加立即执行函数
var arr = [];
for(var i = 0;i<10;i++){
(function(i){
arr[i] = function(){
console.log(i);
}
})(i)
}
arr[0]()
arr[1]()
arr[2]()
arr[3]()
arr[4]()
arr[5]()
arr[6]()
arr[7]()
arr[8]()
arr[9]()
console.log(i)
…运算符计算平均值
//计算平局值
function computedScore(...arg){
// console.log(arg)
let sum = 0;
arg.forEach(function(ele){
sum +=ele
})
return sum / arg.length;
}
console.log(computedScore(97,99,93,92,90,81,88,70))
//去掉一个最高分,去掉一个最低分
function average(...arg){
arg.sort(function(a,b){
return a-b;
})
// console.log(arg)
arg.pop();
arg.shift();
// return computedScore.apply(this,arg);
return computedScore(...arg);
}
console.log(average(97,99,93,92,90,81,88,70))
ES6函数传递表达式面试题
function getContainer(){
console.log('test');
return document.getElementById("container")
}
// 控制台输出几次test?
createElement(undefined,undefined,"sdfsdfsd")
createElement(undefined,undefined,"sdfsdfsd")
createElement(undefined,document.getElementById("container"),"sdfsdfsd")
// 2次 传递以后不会去看默认值的
ES6函数临时性死区面试点
function test(a=b,b){//因为没有声明b而提前使用b就会造成临时性死区//(a,b=a)
console.log(a,b)
}
test(undefined,2)//报错
查看函数有多少次入栈出栈操作
function a(){
console.log('a');
b()
}
function b(){
console.log('b');
c();
}
function c(){
console.log('c');
}
console.log('global');
a()
扩展
//深克隆 会存在问题 对引用值直接转化,不推荐使用
//jqury.api
let obj2 = JSON.parse(JSON.stringify(teachDepartment));
obj2.leader.name = "yin";
console.log(teachDepartment)
console.log(obj2)
$.extend() //抄袭jquery
assign({},,,,,,) //浅克隆
let obj = Object.assign({"asdf":"asdfsdf"},company,teachDepartment);
console.log(obj)
// 知名符号
// 特殊含义的共享符号 通过Symbol的配置得到的
// JavaScript 松散,写法不严谨
// 必须去解决这些严谨性的问题
// 配置底层的实现原理
// console.log(A[Symbol.hasInstance](obj)); //不能这种方式配置
Object.defineProperty(A,Symbol.hasInstance,{
value : function(obj){
console.log(obj);
return false;
}
})
const obj = new A();
console.log(obj instanceof A) //可以去改变instanceof 的值
//2.Symbol.isConcatSpreadable 会对数组的方法产生影响
const arr = [3];
const arr2 = [4,5,6,7]
arr2[Symbol.isConcatSpreadable] = false;
const result = arr.concat(99,arr2) //对两个数组拆分链接成新的数组
console.log(result)
const arr = [1];
const obj = {
0 : 3,
1 : 5,
length : 2,
[Symbol.isConcatSpreadable] : true
}
const result = arr.concat(99,obj)
console.log(result)
//3.Symbol.toPrimitive
const obj = {
a : 1,
b : 2
}
obj[Symbol.toPrimitive] = function(){
return 123
}
console.log(obj + 123)
// 4.Symbol.toStringTag 可以影响Object.prototype.toString的返回值
class Person{
[Symbol.toStringTag] = "Person"
}
const p = new Person()
console.log(p)
const arr = [12,1,2]
console.log(Object.prototype.toString.call(p))