模板字符串
let a = "1"
let str = `${a}`
可以放:
变量、算术计算、三目、对象属性、创建对象、调用函数、访问数组元素
——有返回值的合法的js表达式
不可以放:
没有返回值的js表达式,
也不能放分支/判断、循环等程序结构。
比如: if else for while...等
let
var的问题
- a. 会被声明提前:打乱程序正常的执行顺序
- b. 没有块级作用域:代码块内的变量会超出代码块范围,影响外部的变量
什么是块级作用域:
- 在其他语言里,指除了对象{}和function的{}之外,其余if else、for等分支和循环结构的{}范围程序块{}内的变量,出了所在的程序块{},就不能使用,
但是js没有块级作用域
例子
var t = 0; //全局变量t
function fun1() {
//这个为局部变量t
// var t; //导致下面的 t += 0.8; 不能访问全局变量
t += 0.8; //将fun1的执行时间累加到全局变量t上
if (false) { //不是作用域 + 加上js 是词法作用域,所以t 会被执行前,分析阶段做声明提前
var t = new Date(); //这里会导致 局部变量t 声明提前到fun1(){} 下的第一行,
}
}
function fun2() {
t += 0.3; //将fun2的执行时间累加到全局变量t上
}
fun1();
fun2();
console.log(`t:${t}`)
let 特点
- 因为不会声明提前,所以不能在声明变量之前,提前使用该变量。
//使用var
var a;//undefined
console.log(a);//undefined
a=10; //var a;被声明提前
console.log(a);//10
//使用let
console.log(b); //ReferenceError: Cannot access 'b' before initialization
let b=10;//初始化: 第一次声明变量并赋值
console.log(b);
- 在相同作用域内,禁止声明两个同名的变量!
//使用let
var a = 10;
var a = 100;
console.log(a); //100
let a = 100;// Uncaught SyntaxError: Identifier 'a' has already been declared
console.log(a);
- 因为let底层相当于匿名函数自调,所以,即使在全局创建的let变量,在window 中也找不到!
//使用var
var a=10;
console.log(a); //10
console.log(window.a);//10
//使用let
let b=10;
console.log(b); //10
console.log(window.b);//undefined
//等价于
function () {
let b = 10;
var b = 10; //局部变量
console.log(b); //10 局部变量
console.log(window.b); //undefined
})()
const 的逻辑跟let 一直,只是不能修改而已。
循环遍历 for
普通for
- 可遍历索引数组,
- 可以遍历类数组对象(arguments)——只要下标是数字
var arr = ["11", "22", "33"];
for(var i = 0; i<arr.length; i++ )
console.log(arr[i])
}
//输出 1 2 3
function test() {
for(var i = 0; i<arguments.length; i++ )
console.log(arguments[i])
}
test(1,2,3)
//输出 1 2 3
forEach /map
只能遍历数字下标的索引数组,伪类或对象会失败
function test() {
arguments.forEach(e => {//这里报错,不支持伪数组
console.log(e)
});
arguments.map(e => {//这里报错,不支持伪数组
console.log(e)
});
}
test(1,2,3)
for of
for of会依次取出数组或类数组对象中每个属性值
支持遍历伪数组,因为伪数组也有有序的下标
缺点无法获取key值
function test() {
for(var o of arguments) {
console.log(o)
}
}
test(1,2,3)
let arr = {0:"0" , 1:"1", length:2 }
function test() {
for(var o of arr) //这里会报错 class.js:13 Uncaught TypeError: arr is not iterable 不支持自己定义的类数组对象
console.log(o)
}
}
test()
参数增强
参数默认值
function test(a = 10) {
console.log(a)//10
}
test()
方法参数arguments
function test() {
for(let o of arguments) {
console.log(a)//1,2,3
}
}
test(1,2,3)
缺点 当test是箭头函数 就不能使用 argumnets
剩余参数rest (声明时的形参)
参数收集
适合箭头函数参数不确定场景
let test = (...arr) => {
for(let a of arr) {
console.log(a)//1,2,3
}
}
test(1,2,3)
可以只收集后半部分
let test = (p1,p2,...arr) => {
console.log(p1) //1
console.log(p2) //2
for(let a of arr) {
console.log(a)//3,4,5
}
}
test(1,2,3,4,5)
不能使用箭头函数的场景
- 构造函数不能用
- 对象的方法不能用
- 原型对象方法不能用
- DOM中事件处理函数不能用
- 箭头函数无法用call,apply,bind改变this
- 箭头函数不支持arguments
- 箭头函数没有prototype
展开运算符(spread) (调用时的实参)
展开时,会复制值
展开数组,把每个数组元素都复制一下
Math.max(...[1,2,3]) //是的Math.max支持数组
//等价于
Math.max(1,2,3)
- 复制数组
let arr = [1,2,3]
let arr2 = [...arr]
- 合并数组
let arr = [1,2,3]
let arr2 = [4,5,6]
let arr3 = [...arr,...arr2] //[1,2,3,4,5,6]
- 复制对象
let obj = {name:"jason",age:18}
let obj2 = {...obj}
- 合并多个对象属性
let obj = {name:"jason",age:18}
let obj2 = {weight:120,height:178}
let obj3 = {...obj,...obj2,gender:"男"}
解构赋值
- 对象解构 解决对象多层嵌套访问问题 xx.xx.xx
var user = {
name: "jason11",
age: 18,
address: {
street: "xxxxx街道",
city: "shenzhen"
}
}
console.log(user.address.city)
let {address:add} = user //解构同时重新命名
console.log(add.city)
let {address} = user //如果属性同名可以合并成一个
console.log(address.city)
- 数组解构赋值
var arr=[2021,6,7,9,33];
// 0 1 2 3 4
//仅提取出arr数组中年、月、日三个值单独使用
var [y,m,d]=arr; // 2021,6,7
var [,,d1]=arr; // 7
- 解构赋值失败 解构失败对应的值为undefined
var user = {
name: "jason11",
age: 18,
}
var {
name: name,
address: address, //这里右边的address解构失败为undefined
gender, //这里右边的gender解构失败为undefined
} = user
console.log(name) //jason1
console.log(address) //undefined
console.log(gender) //undefined
参数解构
形参和实参都使用对象的方式传入
//定义 参数使用对象定义
function $ajax({
url: url,
type: type,
data: data,
datatype: datatype
}){
console.log(url)
console.log(type)
console.log(data)
console.log(datatype)
}
//调用
$ajax({
url: "https://www.baidu.com",
// data: data, //这里可以省略data参数
datatype: "json",
type: "get", //并顺序不一定有序
});
//当然 方法的参数如果形参和实参一致,可以合并
function $ajax({
url,
type,
data,
datatype
}){
console.log(url)
console.log(type)
console.log(data)
console.log(datatype)
}
class
es6的类 本质上还是 构造函数.
//es6的class 定义类创建实例
class User {
constructor(name,age){
this.name = name
this.age = age
}
info(){
console.log(this.name,this.age)
}
}
let user = new User()
console.log(user)
//构造函数创建实例
function User2(name,age){
this.name = name
this.age = age
}
User2.prototype.info = function () {
console.log(this.name,this.age)
}
let user2 = new User2()
console.log(user2)
两者的底层是一致的。
在原型上都有constructor指向构造函数的定义
静态属性
class 通过定义static 变量实现。底层实现还是直接挂在构造函数上
//es6 class 定义静态变量
class User {
constructor(){
}
static a = "111"
}
console.log(User.a)
//等价于 构造函数直接挂属性
function User2(){
}
User2.a = "111"
console.log(User.a)
继承extends
为什么继承不用单词inherit,而用extends。 因为代码继承更多是为了扩展extends。
为什么用super表示父级,而不用father。 因为super表示的是数学中的超集合的意思,如猫科动物就是一个超集,老虎就是子集
- 使用extends 继承父类
- 在子类方法的构造函数调用super(),初始化父类的实例,不然会报错
class Person{
constructor(name,age){
this.name = name
this.age = age
}
eating(){
console.log(this.name + " eating~")
}
}
class User extends Person{
constructor(name,age,address){
super(name,age);//这里如果不调用super()会报错
//Uncaught ReferenceError: Must call super constructor in derived class before accessing 'this' or
this.address = address;
}
running(){
console.log(this.name + " runnig~")
}
}
let user = new User("jason",18,1111)
user.running() //子类方法
user.eating() //父类方法
当没有主动写子类的constructor,es6会自动添加constructor和super的调用
class User extends Person {
}
// 等同于
class User extends Person {
constructor(...args) {
super(...args);
}
}
判断数组
let obj = {name:"xxx"}
let arr = [1,2,3]
//不能用typeof 判断数组
console.log(typeof obj) //true
console.log(typeof arr) //true
//使用实例__proto__ 原型链判断 ok
console.log( obj.__proto__ === Array.prototype) //false
console.log( arr.__proto__ === Array.prototype) //true
//使用constructor 原型链判断
console.log( obj.__proto__.constructor === Array ) //false
console.log( arr.__proto__.constructor === Array ) //true
//使用 Object.getPrototypeOf 原型链判断 ok
console.log( Object.getPrototypeOf(obj) === Array.prototype) //false
console.log( Object.getPrototypeOf(arr) === Array.prototype) //true
//使用 prototype.isPrototypeOf 原型链判断 ok
console.log( Array.prototype.isPrototypeOf(obj) ) //false
console.log( Array.prototype.isPrototypeOf(arr) ) //true
//使用 toString.call 原型链判断
console.log( Object.prototype.toString.call(obj) == "[object Array]" ) //false
console.log( Object.prototype.toString.call(arr) == "[object Array]" ) //true
//使用Array.isArray
console.log( Array.isArray(obj) ) //false
console.log( Array.isArray(arr) ) //true