ES:ECMAJavaScript的缩写
babel 转译器,将ES6–> ES5
- kiss原则:keep it simple,stupid
声明的变量具有块级作用域,类似 { }
if(true){
let a=10;
}
console.log(a);// a is not defined
console.log(a); // a is not defined
let a=20;
简单而又怪异的题目:
var a = a;
console.log(a); // undefined
let b = b; // b在没有赋值之前就引用了,而这是一个死区 (这个是自右向左执行的欸!把b赋值给b,而又因为b还没定义,所以报错)
console.log(b); // 报错
//===============================
function test(x = y,y = 2){
console.log(x,y); // 报错,因为这依然是个死区。它将y赋值给x,但是y还没有被定义
}
typeof有可能会报错了
console.log(typeof a); // 报错
let a;
ES6语法:能够在函数的参数进行赋值,给它默认值
就是,在块级作用域中,使用let声明变量,这个变量会与这个块级整体进行绑定,不受外部影响。
var num=10;
if(true){
console.log(num); //会报错,num is not defined
let num=20;
}
(同一作用域:块级作用域、全局作用域、函数作用域)
例子:
function test(a){
let a = 10;
console.log(a); //报错了,形参处定义了a
}
test();
//===============================
function test(a){
{
let a = 10;
}
console.log(a); //undefined
}
test();
for(var i = 0; i < 10; i++){
i = 'a';
console.log(i);
}
打印出1个a,分析:
var i = 0;
for( i < 10; ){
i = 'a'; // 被重新赋值了
console.log(i); // 'a'
i++; //'a1'
}
//-----------------------------------------
for(var i = 0; i < 10; i++){
var i = 'a';
console.log(i);
}
打印出1个a,分析:
var i = 0;
for( i < 10; ){
var i = 'a'; // 被重新赋值了
console.log(i); // 'a'
i++; //'a1'
}
”表达式也是一块作用域“
情况:
for(let i = 0; i < 10;i++){
i = 'a';
console.log(i); // 打印出1个a
}
分析:
{
let i = 0;
{
// ① 子级作用域可以使用父级作用域中的变量
// ② 因为子级里头没有嘛,所以他拿了父子的,并更改为'a'
// 再执行i++ 的时候就变为 'a1'
i = 'a';
}
}
情况:
for(let i = 0; i < 10; i++){
var i = 'a';
console.log(i); // 报错
}
分析:
{
var i; // 变量提升
let i = 0; // 相当于声明了两次,所以会报错
{
var i = 'a';
}
}
情况:
for(let i = 0; i < 10; i++){
let i = 'a';
console.log(i); // 打印出10个a
}
分析:
{
let i = 0; // 相当于声明了两次,所以会报错
{
let i = 'a';
}
}
打印 0-9 的情况:重新覆盖
var arr = [];
for(var i = 0; i < 10; i++){
arr[i] = function(){
console.log(i);
}
//到最后 i = 10;
}
// 但因为此处重新声明了一次,所以i被覆盖了
for(var i = 0; i < 10; i++){
arr[i]();
}
函数声明只能在顶层作用域和函数作用域当中
函数声明提升是在当前作用域基础上的
作用:声明常量。内存地址不能变化的量
(简单数据类型的值是不能变化的,遇上复杂数据类型时只可改里头的内容,“保证指针不变”)
顶层对象——window
(不同的环境中顶层对象是会不一样的)
对于ES5:
传入实参0,会被读成布尔值,这样就体现不了0了。因此这种方式在一些情况下就不合适
function foo(x,y){
x = x || 1;
y = y || 2;
console.log(x+y);
}
foo(); // 3
foo(5,6); // 11
foo(5); // 7
foo(null,6); // 7
foo(0,5); // 6
“参数可以赋值” (待完善)
参数可赋值,那就可以传变量,那参数的作用域是怎样的?(默认值作用域问题)
可以理解为它是 let声明的
惰性求值: 函数的参数为表达式的情况下,参数加载的方式是惰性求值的方式,每(执行)一次都要重新计算表达式的值
解构失败:变量多了,没有“匹配”到的就是undefined
不完全解构: 值多了
解构赋值中依然可以给默认值,给值了就不会找默认值
关于数组解构例子:
let [a = 6] = [];
console.log(a); // 6
// 浏览器默认undefined为没有赋值
let [a,b = 6] = [1,undefined];
console.log(a,b); // 1, 6
let [a,b = 6] = [1,null];
console.log(a,b); // 1, null
let b = 5;
let [b = 6, c = b] = [];
console.log(b,c); // 报错,b已经被定义了
let [x = y, y = 1] = [];
console.log(x,y); // 报错,暂时性死区问题
let firstName = 'Xing';
let secondName = 'is';
let name = 'Xing is';
let person = {
[firstName + secondName] : name
}
console.log(person);
对象的解构是不存在顺序的,是根据属性名来“匹配”的
let a;
({
a} = {
a:1};)
console.log(a);
数组也是特殊的对象,也能进行解构赋值
let arr = [1,2,4];
let {
0: first, [arr.length - 1]:last} = arr;
console.log(first,last);
[(b)] = [3]; // 这样可以匹配成功
console.log(b); // 3
([b]) = [3];
console.log(b); // 不能成功,因为匹配的规则不一样,左边是表达式,右边是数组
({
a: (b) = {
}}); // 本身并没有匹配,这个是默认值
// 对象下面有个属性,属性默认值为{}
console.log(b); // {}
模式匹配:
首先要模式一样才能进行匹配
其次,有括号的时候一定要注意 这括号起的是什么作用
模式匹配,本质上是声明变量
函数传参也相当于一个变量的声明,声明的方式是通过let来声明的
计算属性解构:
模式匹配可以匹配同源属性(同一个源属性)
var x = 200,
y = 300,
z = 100;
var obj1 = {
x: {
y: 42
},
z: {
y: z // 这个z是多少哇?
}
};
({
y: x = {
y: y}} = obj1); // obj1中没有y这个属性。所以就默认值
({
z: y = {
y: z}} = obj1); //100
({
x: z = {
y: x}} = obj1);
console.log(x.y, y.y, z.y);
function foo({
x = 10} = {
}, {
y} = {
y: 10}){
console.log(x, y);
}
foo(); // 10 10
foo({
},{
}); // 10 undefined;
foo({
x: 2}, {
y: 3}); // 2 3
注意:
({
x = 10} = {
}); ==>({
x: x = 10} = {
});
({
y} = {
y: 10}); ==> {
y: y} = {
y: 10}
转成类数组了:
const [a, b, c, d, e] = 'hello';
console.log(a, b, c, d, e); // h,e,l,l,o
let {
length : len} = 'hello'; // 转成类数组了,类数组具有length属性
console.log(len); // 5 解构出来了就表示隐式转换
数字 对应的包装类 Num
let {
toString: s} = 123;
console.log(s === Number.prototype.toString); // true
布尔值 对应的包装类 Boolean
let {
toString: s} = false;
console.log(s === Boolean.prototype.toString); // true
undefined 和 null 都没有隐式转换
let {
prop} = undefined;
console.log(prop); // 报错
let {
prop} = null;
console.log(prop); // 报错
这也是为什么调用函数时不传内容会报错,是因为空的内容并不能进行隐式转换
函数参数给了默认值的时候:
function test(a, b, c = 1, d, e, f){
}
tset();
console.log(test.length); // 2
格式:
( ) => {
}
const fn = ( ) => {
}
如何调用?
赋值给一个变量
箭头函数可以和解构赋值结合来用
在箭头函数中不存在arguments (侧面说明,箭头函数不是用function来声明的)
作用:
let ary1 = [1,2,3];
let ary2 = [4,5,6];
let ary3 = [...ary1,...ary2];
//或 ary1.push(...ary2);
let divs = document.getElementsByTagName('div');
divs=[...divs];
1. 当箭头函数只执行一条,且想返回一个对象的时候(用括号把它包起来)
(a,b) => ({
a:3, b:4})
不是用function来定义的,是用胖箭头来定义的
this指向是固定化的,函数的内部并没有自己的this,只能通过父级作用域来获取到this,闭包的this
例1:
function fn(){
return (a) => {
console.log(this.a);
}
}
var obj1 = {
a: 2};
var obj2 = {
a: 3};
var bar = fn.call(obj1);
var bara = fn();
bar.call(obj2); // 2 (这样不会更改this指向,还是根据外层函数的this)
bar.call(obj2); // undefined
例2:▲!注意理解这个
const person = {
eat(){
console.log(this);
}
drink:() => {
console.log(this);
}
}
person.eat(); // person. this一开始是指向window的,在此处person调用eat时隐式的转换成person
person.drink(); // window. (对象先挂在全局,然后赋值给person,所以它的外层this是指向window)
1: (这个需要先去回顾一下)
传统写法:
(function(){
// 构造函数
function Button(){
this.button = document.getElementById("button");
}
Button.prototype = {
init(){
this.bindEvent();
},
bindEvent(){
this.button.addEventListener('cilck',this.clickBtn.bind(this),false);
},
clickBtn(){
console.log(this);
}
}
new Button().init();
})();
使用箭头函数的写法:
(function(){
function Button(){
this.button = document.getElementById("button");
}
Button.prototype = {
init(){
this.bindEvent();
},
bindEvent(){
this.button.addEventListener('cilck',(e) => this.clickBtn(e),false);
},
clickBtn(e){
console.log(e);
console.log(this);
}
}
new Button().init();
})();
2.
在这里插入代码片