1. 由一对 { } 界定的语句叫做块语句,这在其他编程语言中叫做复合语句。
一般理解的块: 块:由{}包括住 if(){}:是一个块 for(){}:是一个块 这些也可理解成块: 一个函数:函数作用域 script标签:全局作用域
2. JS中用var声明的变量是没有块级作用域的,只有函数作用域和全局作用域。
var x = 1; { var x = 2; } console.log(x); //会输出2,因为块中的var语句与块前面的var语句作用域相同 //在C或Java中,这段代码会输出 1 //这代码证明了var没有块作用域3. 相比之下,使用 let 和 const 声明的变量是有块级作用域的。
let x = 1; { let x = 2; } console.log(x); // 输出 1 // x被限制在块级作用域中 // 这里将let换成const结果也一样4. 经典的例子(背住):
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () {console.log(i);}; } a[0](); // 10 a[1](); // 10 a[6](); // 10 /********************/ var i 在a【i】的时候已经赋值了,在函数的花括号{}没有赋值所以。 var a = []; for (let i = 0; i < 10; i++) { a[i] = function () {console.log(i);}; } a[0](); // 0 a[1](); // 1 a[6](); // 6 ```javascript **//第一个中的i是没有块级作用域的,全局只有一个变量i,每次循环的{}中的代码都指向同一个i //第二个中的i是有块级作用域的,当前的i只在本轮循环有效,每次循环的{}中的代码都指向不同的i //值得注意的是:for的()是一个父作用域,{}是一个子作用域 //因为当前的i只在本轮循环有效,所以let定义的i在每次循环都会重新定义一遍,每次都是一个新变量**5. 补充:
foo('outside'); // TypeError: foo is not a function { function foo(location) { console.log('foo is called ' + location); } foo('inside'); // 正常工作并且打印 'foo is called inside' } //使用function时也有块级作用域6. var变量提升,let变量暂时性死区:
//var会发生变量提升现象,将变量提升到函数顶部或全局顶部 console.log(foo); //输出undefined,不会报错 var foo = 2; //let没有这种情况,必须在let声明后调用该变量 (const和let一样) //let和const的这种现象称为暂时性死区 //从块开始到let声明之间是“暂存死区” console.log(bar); //报错ReferenceError let bar = 2;7. let的错误 在同一个作用域中用let重复定义一个变量将引起TypeError
if (x) { let foo; let foo; // TypeError thrown. }**• 必须在声明的同一语句中指定它的值 • const声明创建一个值的只读引用,也就是说只能通过const定义的变量来读这个值,不能修改这个值,但是如果这个值本身发生了变化,那么const定义的变量所对应的值也就跟着变化,比如当引动内容是对象的情况下。 • 一个常量不能和它所在作用域内的其他变量或函数拥有相同的名称。 // 下面错误,常量要求有一个初始值
const FOO; // 下面正确,常量可以定义成对象 const MY_OBJECT = {"key": "value"}; // 下面错误,改变常量会失败 MY_OBJECT = {"OTHER_KEY": "value"}; // 对象属性并不在保护的范围内,下面会成功执行(对象本身发生变化) MY_OBJECT.key = "otherValue"; // 定义一个常量 const MY_FAV = 20; // 不能同名,会出错 var MY_FAV = 20; // 也会报错 let MY_FAV = 20;**基础语法:
//一般语法: (参数1, 参数2, …, 参数N) => { 函数声明 } //相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; } (参数1, 参数2, …, 参数N) => 表达式(单一) // 当只有一个参数时,圆括号是可选的: (单一参数) => {函数声明} 单一参数 => {函数声明} // 没有参数的函数应该写成一对圆括号 () => {函数声明}高级语法:
(*注: 字面量一般指[1, 2, 3] 或者{name: "mdn"} 这种简洁的构造方式) *//加括号的函数体返回对象字面表达式: 参数=> ({foo: bar}) //支持剩余参数和默认参数 (参数1, 参数2, ...rest) => {函数声明} (参数1 = 默认值1,参数2, …, 参数N = 默认值N) => {函数声明} //同样支持参数列表解构 let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6例子: 箭头函数比较适用于原本需要匿名函数的地方,比如用在数组内置方法map、filter、forEach、reduce的回调函数中
var elements = [ 'Hydrogen', 'Helium', 'Lithium', 'Beryllium' ]; elements.map(function(element) { return element.length; }); // 返回数组:[8, 6, 7, 9] // 上面的普通函数可以改写成如下的箭头函数 elements.map((element) => { return element.length; }); // [8, 6, 7, 9]重点注意: this 对象的指向是可变的,但是在箭头函数中,它是固定的。箭头函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。 三. 参数处理 1. 给函数设置默认参数值 在ES6以前,我们想要给参数设置默认值得这么做:
function multiply(a, b) { //JavaScript中函数的参数默认是undefined //没有值传入或者undefined被传入给b时给b一个默认值 b = (typeof b !== 'undefined') ? b : 1; return a * b; } multiply(5, 2); // 10 multiply(5); // 5现在学习了ES6之后就变得很简单:
function multiply(a, b = 1) { return a * b; } multiply(5, 2); // 10 multiply(5); // 5 注意参数的传递是从左到右的: function f(x = 1, y) { return [x, y]; } f(); // [1, undefined] f(2); // [2, undefined]2. 剩余参数(…) 剩余参数语法允许我们将一个不定数量的参数表示为一个数组。 语法:
function(a, b, ...theArgs) { ... } //必须是函数的最后一个参数以...为前缀 //它是由剩余实参组成的Array数组 //这个例子中的theArgs收集第三个以及之后的所有实参实例:
function fun1(...theArgs) { alert(theArgs.length);//可以使用所有数组属性方法 } fun1(); // 弹出 "0", 因为theArgs没有元素 fun1(5); // 弹出 "1", 因为theArgs只有一个元素• 创建对象时可以简化方法和重名的键值对
//一般情况 var name="pan"; var age=20; var people = { name: name, age: age, getName: function() { console.log(this.name) } }; //ES6简化后 var name="pan"; var age=20; var people = { name, age, getName(){ console.log(this.name) } };• 计算属性名(属性名可以用表达式) • 对象中如果属性名要设置为一个变量,必须要用[]包裹
var i = 0; var a = { ["foo" + ++i]: i, ["foo" + ++i]: i, ["foo" + ++i]: i }; console.log(a.foo1); // 1 console.log(a.foo2); // 2 console.log(a.foo3); // 3解构赋值语法是一种 Javascript 表达式,它使得将值从数组,或属性从对象,提取到不同的变量中,成为可能。 目的就是将数组中的值或对象中的属性方便的提取到其他变量中 • 解构数组语法:
var x, y; [x, y] = [1, 2, 3]; console.log(x); // 1 console.log(y); // 2 //或者: var [x, y] = [1, 2, 3]; console.log(x); // 1 console.log(y); // 2小技巧(不用临时变量也能交换变量):
var a = 1; var b = 3; //等价于[a,b]=[3,1] [a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1• 解构对象语法:
var o = {p: 42, q: true}; var {p, q} = o; console.log(p); // 42 console.log(q); // true注意:对象解构与数组解构有一个重要的不同点,数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。下面是一个 JS 文件,里面使用export命令输出变量。 • 导出export
// profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year = 1958; export {firstName, lastName, year}; //上面代码是profile.js文件,保存了用户信息, //ES6将其视为一个模块,里面用export命令对外部输出了三个变量。 //除了这种普通变量,输出的变量还可以是一个函数。 //另外,export语句输出的接口,与其对应的值是动态绑定关系, //即通过该接口,可以取到模块内部实时的值。 //最后,export/import命令不能在块级作用域内,可以在全局作用域任何位置。• 导入import import语句只能在声明了type="module"的script的标签中使用。 动态导入:import(),它不需要依赖type="module"的script标签。 按照一定的条件或者按需加载模块的时候,动态import()是非常有用的。 而静态的import是初始化加载的最优选择。
// main.js import {firstName, lastName, year} from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; } //上面代码的import命令,用于加载profile.js文件,并从中输入变量。 //import命令接受一对大括号,里面指定要从其他模块导入的变量名。 //大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同。 注意:import语句会执行所加载的模块 import './lodash.js'; //执行lodash模块,但是不输入任何值整体导入:
import * as myModule from './my-module.js';//不用加大括号{} //将my-module里面输出的变量/函数, //输入到myModule这个对象中作为属性/方法。 //通过myModule.属性名/myModule.方法()来调用。 • 默认导出/导入默认值: 告别使用import命令的时候,用户需要知道所要加载的变量名或函数名的现状 //先用export default语法默认导出 //既然是默认输出,那么一个模板只能使用一次 export default function foo() { console.log('foo'); } //导入默认值 import customName from './default.js';//不用加大括号{} customName(); // 'foo'ES6 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为JavaScript引入新的面向对象的继承模型。 ES6中面向对象的写法就是普通的后端语言面向对象的写法, 定义类用class关键字,继承通过extends关键字,以及super关键字
// 一:类的定义和使用 // 1.通过class定义类 // 2.在类中通过constructor定义构造方法,构造方法来初始化类的属性 // 3.通过new来创建类的实例 // 二:类的继承意义何在? // 子类继承父类之后可以获得父类的属性和方法 // 三:类的继承具体语法 // 1.通过extends来实现类的继承 // 2.通过super调用父类的构造方法 // 3.重写可以覆盖父类中的继承的方法 class Person{ // 调用类的构造方法constructor方法 // 初始化类的属性 constructor(name,age){ this.name=name; this.age=age; } // 定义原型上的方法 showName(){ console.log(this.name,this.age); } } var person=new Person("林冲",29); console.log(person); person.showName() // 定义一个子类 // 继承的话会继承父类的属性和方法 class Student extends Person{ constructor(name,age,stuNum){ 利用super关键字去继承父类当中的constructor方法 super(name,age);//调用父类上的构造方法constructor方法 this.stuNum=stuNum; } // showName(){ // // 在子类自身定义方法 // console.log(this.name,this.age,this.stuNum); // } } var student=new Student("夏东海","18","1314") student.showName() // 如上如果我们调用showName方法时,因为是继承父,所以我们如果之前在父上写了showName方法的时候在子类调showName方法的时候 // 会直接调用父中定义好的showName方法。如果子类上也定义了同样名字的showName方法的时候,子类的同名方法就会直接覆盖掉父类的方法。 // 如果父类有子类没写showName方法就直接用父类的showName方法,如果父类写了showName方法,子类也写了showName方法那么就会调用子类内部的方法 // 总结子类重写父类已有的方法父类的同名方法会被覆盖掉• 如何创建类:
class Rectangle { constructor(height, width) { //构造方法 this.height = height; this.width = width; } } //或者 let Rectangle = class { constructor(height, width) { //构造方法 this.height = height; this.width = width; } };• 如何使用类:
class Rectangle { // constructor constructor(height, width) { this.height = height; this.width = width; } // Getter get area() { return this.calcArea() } // Method calcArea() { return this.height * this.width; } } const square = new Rectangle(10, 10); console.log(square.area); // 100• 类的静态方法: 不能通过一个类实例调用静态方法(这里可Java语法有区别)
class Point { constructor(x, y) { this.x = x; this.y = y; } static distance(a, b) { //static语法 const dx = a.x - b.x; const dy = a.y - b.y; return Math.hypot(dx, dy); } } const p1 = new Point(5, 5); const p2 = new Point(10, 10); //直接通过类名调用 console.log(Point.distance(p1, p2));• 类的继承(extends)
class Animal { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } class Dog extends Animal { console.log(this.name + ' barks.'); } } var d = new Dog('Mitzie'); // 'Mitzie barks.' d.speak();• 调用父类属性方法(super)
class Cat { constructor(name) { this.name = name; } speak() { console.log(this.name + ' makes a noise.'); } } class Lion extends Cat { speak() { super.speak(); console.log(this.name + ' roars.'); } }