JS 面向对象(ES6)学习笔记

235 阅读16分钟

附上一篇文章:www.cnblogs.com/soyxiaobi/p…

面向对象编程介绍

面向对象特征:封装性、继承性、多态性

ES6中的类和对象

1.创建类(ES6之前的创建对象 方法见之前js的笔记)

类名首字母大写

创建类 类名后面不要加小括号 ; 生成示例 类名后面加小括号

多个函数方法之间不需要添加逗号

class Star {
//构造函数constructor
    constructor(uname,age) {
  //类中的函数不需要加function
        this.uname = uname;
        this.age = age
    }

    sing() {                 //添加方法
        console.log('我唱歌');
    }
}
var ldh = new Star('刘德华',30);
ldh.sing();

2.类的继承

class Father {
    constructor(){
    }
    money() {
        console.log('100块钱');
    }
}
class Son extends Father {
}
var son = new Son();
son.money(); //son继承了father里面的方法(儿子继承了爹的一百块钱)

super 关键字

用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数

        class Father {
            constructor(x,y){
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x +this.y);
            }
        }
        class Son extends Father {
            constructor(x,y){
                super(x,y); //调用父类的constructor,才能把x y赋值给父类构造函数,才能调用sum
            }
        }
        var son = new Son(1,2);
        son.sum();

继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的方法。如果子类没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)

class Father {
    say() {
        return '我是爸爸';
    }    
}
class Son extends Father {
    say() {
        console.log('我是儿子');
        console.log(super.say() + '的儿子'); //super调用父类的普通函数
    }
}
var son = new Son();
son.say();

**注意:**子类在构造函数中使用super,必须放在this前面(必须先调用父类的构造方法,再使用子类构造方法)

3.ES6中类和对象的三个注意点

  1. 在ES6中类没有变量提升,所以必须先定义类,才能通过类实例化对象
  2. 类里面的共有的属性和方法一定要加this使用
  3. 类里面的this指向问题:constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者,在这些方法里可以用that

构造函数和原型

1.构造函数

JavaScript的构造函数中可以添加成员。在构造函数本身上添加的成员叫做静态成员只能由构造函数本身来访问。在构造函数内部用this创建的成员叫实例成员,只能由实例化的对象来访问

function Star (uname,age) {
    this.uname = uname;
    this.age = age;
    this.sing = function() {
        console.log('我会唱歌');
    }
}
var ldh = new Star('刘德华',18);  //uname  age sing就是实例成员
console.log(ldh.uname); //只能通过实例化对象ldh来访问
ldh.sing();

Star.sex = '男';
//静态成员只能通过构造函数来访问
console.log(Star.sex); 
//不能通过对象来访问
console.log(ldh.sex); //undefined

构造函数创建对象很好用,但是存在浪费内存 的问题(相同函数却要各自开辟一个空间存放)

2. 构造函数原型 prototype  与对象原型__proto__

构造函数通过原型分配的函数是所有对象所共享的。

JS中规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象里面的所有属性和方法,都被构造函数所拥有。

我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。

function Star (uname,age) {
    this.uname = uname;
    this.age = age;
}

Star.prototype.sing = function() {
    console.log('我会唱歌');
}

var ldh = new Star('刘德华',18);  
var zxy = new Star('张学友',19);
ldh.sing(); //可以正常输出
console.log(ldh.sing === zxy.sing);//true

//一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型对象的身上

对象都会有一个属性__proto__ 指向构造函数的prototype原型对象。之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为__proto__原型的存在。

__proto__对象原型和原型对象prototype是等价的

//接上一个代码块
console.log(ldh.__proto__ === Star.prototype);  //true

方法的查找规则:先看ldh对象身上是否有sing方法,如果没有,因为__proto__的存在,就去
构造函数原型对象prototype身上去找sing方法

3.constructor构造函数

对象原型(__proto__)和构造函数(prototype)原型对象 里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。

function Star (uname,age) {
    this.uname = uname;
    this.age = age;
}
--------------------------------------------------
Star.prototype.sing = function() {
    console.log('我会唱歌');
}

Star.prototype.movie = function() {
    console.log('我会演电影');
}console.log(Star.prototype.constructor);
console.log(ldh.__proto__.constructor);    //依旧会指向构造函数Star

-------------------------------------------------
Star.prototype = {  //此时prototype被自己写的对象覆盖掉了,constructor也没了
    constructor:Star; //所以需要手动将constructor指回构造对象
    sing:function(){
        console.log('我会唱歌');
    },
    movie:function(){
        console.log('我会演电影');
    }
}

4.构造函数、实例、原型对象三者之间的关系

5.原型链

JS的成员查找机制

  1. 首先看这个对象本身有没有该属性(或者方法)
  2. 如果没有,就看他的原型(对象的__proto__指向的prototype原型对象)
  3. 如果还没就去查找原型对象的原型(Object的prototype)
  4. 以此类推直到找到null

6.原型对象中的this指向

在构造函数中,里面的this指向的是实例对象

在原型对象函数里的this,指向的还是实例对象

function Star (uname,age) {
    this.uname = uname;
    this.age = age;
}
var that;
Star.prototype.sing = function(){
    console.log('我会唱歌');
}
var ldh = new Star('刘德华',18);
// 构造函数中,this指向的是对象实例ldhldh.sing();
//原型对象函数中的this,指向的也是对象实例ldh console.log(that === ldh); // ture

7. 扩展内置对象

可以通过原型对象,对原来的内置对象进行自定义扩展。比如给数组增加自定义求和的功能。(只能用Array.prototype.xxx = function(){}的方式

console.log(Array.prototype);Array.prototype.sum = function() {    var sum = 0;    for(var i = 0;i< this.length;i++){        sum += this[i];    }    return sum;}var arr = [1,2,3];console.log(arr.sum());

继承

ES6之前没有给我们提供extends继承。我们可以通过构造函数+原型对象模拟继承,被称为组合继承

1.call()

调用这个函数,并且修改函数运行时的this指向

fun.call(thisArg,arg1,arg2,...)

thisArg:当前调用函数this的指向对象

arg1,arg2 :传递的其他参数

function fn() {
    console.log('我想喝拿铁咖啡');
    console.log(this);
}
var o = {
    name:'andy'
};
fn.call(); // this指向window
fn.call(o); //this指向对象o

2.继承

通过call()把父类型的this指向子类型的this,实现子类型继承父类型的属性

function Father(uname,age) {
    //this指向父构造函数的对象实例
    this.uname = uname;
    this.age = age;
}
function Son(uname,age) {    //this指向子构造函数的对象实例
    Father.call(this,uname,age);
}
var son = new Son('刘德华',18);
console.log(son);

继承父类的方法:让子类的prototype指向父类的一个实例对象

function Father(uname,age) {    //this指向父构造函数的对象实例
    this.uname = uname;
    this.age = age;
}
Father.prototype.money = function() { //父类型的方法
    console.log(100000);
}
function Son(uname,age) {    //this指向子构造函数的对象实例
    Father.call(this,uname,age);
}
Son.prototype = new Father(); //让子类的prototype指向一个新建的父类的实例对象
//如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的构造函数
Son.prototype.constructor = Son; 
Son.prototype.exam = function() {//这是子类独有的方法 且不影响父类
    console.log('孩子要考试啦');
}
var son = new Son('刘德华',18);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);


这个例子中原型链:实例对象son.__proto__  -> Son.prototype -> Father.prototype  -> Object.prototype -> null

3.ES5中新增的方法

  数组方法:forEach() map() filter() some() every()

  字符串方法: trim() 去除左右两侧的空格

  对象方法:Object.keys() 用于获取对象自身所有属性;

                   defineProperty()用于定义新属性或者修改原有的属性

4. 各种继承方式

1.原型链继承:将父类实例作为子类的原型

优点:父类中的方法可以复用

缺点: 

  • 创建子类实例的时候,不能传父类的参数(比如name,不同子类的name不会因为参数不同而不同)

  • 子类实例共享了父类构造函数的引用属性,如arr

  • 无法实现多继承

    function parent(name) { this.name = name || '父亲'; this.arr = [1]; } Parent.prototype.say = function(){ console.log('hello'); } function Child(like) { this.like = like; }

    Child.prototype = new Parent(); //核心 Child.prototype.constructor = Child; //核心

    let boy1 = new Child(); let boy2 = new Child();

    //缺点1:不能向父类构造函数传参 cconsole.log(boy1.name, boy2.name, boy1.name === boy2.name); // 父亲,父亲,true

    //缺点2: 子类实例共享了父类构造函数的引用属性,比如arr boy1.arr.push(2); console.log(boy2.arr); //[1,2] 对boy1操作,boy2的arr也会改变

2.借用构造函数

核心: 子类构造函数里用父类构造函数加call

优点:

  • 创建子类实例,可以向父类构造函数传参
  • 子类实例不共享父类构造函数的引用属性,如arr
  • 可以实现多继承(通过多个call或者apply继承多个父类)

缺点:

  • 父类的方法不能复用:由于方法在父类构造函数中定义,导致方法不能复用(因为每次创建子类实例都要创建一遍方法)比如say方法。方法应该要复用、共享

  • 子类实例,继承不了父类原型上的属性(因为子类的prototype没有指向父类的

    function Parent(name) { this.name = name || '父亲'; this.arr = [1]; this.say = function (){ console.log('hello'); } }

    function Child(name,like) { Parent.call(this,name); //核心 this.like = like; }

    let boy1 = new Child('小红','Apple'); let boy2 = new Child('小明','orange');

    //优点1:可以向父类构造函数传参 cconsole.log(boy1.name, boy2.name, boy1.name === boy2.name); // 小红,小明,false

    //优点2: 子类实例不共享了父类构造函数的引用属性,比如arr boy1.arr.push(2); console.log(boy1.arr,boy2.arr); //[1,2] [1]

    //缺点1:方法不能复用 console.log(boy1.say === boy2.say) //false boy1和boy2的say方法是独立的 不是共享的

    //缺点2:不能继承父类原型上的方法 Parent.prototype.walk = function(){ console.log('我会走路'); } boy1.walk; //undefined 说明实例不能获取父类原型上的方法

方法3:组合继承

核心:1.借助父类构造函数,2.将父类实例作为子类原型(即上面两种方法均使用)

优点:

  • 保留构造函数的优点:创建子类实例,可以向父类构造函数传参
  • 保留原型链的优点:父类方法定义在父类的原型对象上,可以实现方法复用。

缺点:

由于调用了两次父类的构造方法,会存在多余的一份父类实例属性。

function parent(name) {
    this.name = name || '父亲';
    this.arr = [1];
}
Parent.prototype.say = function(){
    console.log('hello');
}
functioin Child(name,like){
    Parent.call(this,name); //核心,第一次调用
    this.like = like;
}
Child.prototype = new Parent(); //核心 第二次调用
Child.prototype.constructor = Child //修正constructor的指向

let boy1 = new Child('小红','Apple');
let boy2 = new Child('小明','orange');
//优点1: 可以向父类构造函数传参数
cconsole.log(boy1.name, boy1.like); // 小红,apple
///优点2: 可以复用父类原型上的方法
cconsole.log(boy1.say === boy2.say) //true
//优点3: 不共享父类的引用属性,如arr
boy1.arr.push(2);
console.log(boy1.arr, boy2.arr); // [1,2] [1]

//缺点1:由于调用了两次父类的构造方法 会存在一份多余的父类的实例属性

方法4:组合继承优化

核心: 砍掉父类的实例属性,让子类的原型直接指向父类的原型

优点:

  • 只调用一次父类的构造函数
  • 保留构造函数的优点
  • 保留原型链的优点

缺点:

  • 修正构造函数的指向之后,父类实例的构造函数指向,同时也发生了改变

    function parent(name) { this.name = name || '父亲'; this.arr = [1]; } Parent.prototype.say = function(){ console.log('hello'); } functioin Child(name,like){ Parent.call(this,name); //核心,唯一一次调用 this.like = like; } Child.prototype = Parent.prototype //核心 子类原型和父类原型,实质上是同一个 let boy1 = new Child('小红','Apple'); let boy2 = new Child('小明','orange'); let p1 = new Parent('小爸爸');

    //优点1:可以向父类构造函数传参数 console.log(boy1.name , boy1.like) //小红,Apple //优点2:可以复用父类原型上的方法 console.log(boy1.say === boy2.say) //true

    //缺点1:当修复子类构造函数的指向后,父类实例的构造函数指向也会跟着改变

    没修复以前: console.log(boy1.constructor); //Parent 修复代码: Child.prototype.constructor = Child; 修复之后: console.log(boy1.constructor); //Child console.log(p1.constructor); //Child(父类的也被改了)

方法5: 寄生组合继承 ---完美方式

function Parent(name) {
    this.name = name || '父亲';
    this.arr = [1];
}
Parent.prototype.say = function() {
    console.log('hello');
}functioin Child(name,like){
    Parent.call(this,name); //核心
    this.like = like;
}

//核心 通过创建中间对象,子类原型和父类原型就会隔离开,有效避免方法4的缺点
Child.prototype = Object.create(Parent.prototype);//这里是修复构造函数指向的代码
Child.prototype.constructor = Child

let  boy1 = new Child('小红','apple');
let  boy2 = new Child('小明','orange');
let  p1 = new Parent('小爸爸');

函数进阶

1.函数定义方式

//1.自定义函数(命名函数)
function fn() {};

//2.函数表达式(匿名函数)
var fun = function(){};

//3.利用new Function('参数1','参数2',...,'函数体');  //仅供了解
var fn = new Function('a','b','console.log(a + b)');
fn(1,2);
//所有函数都是 Function的实例

2.this指向问题

改变函数内部this指向:

bind()  call() apply()

1)call()  略

2)apply()

fn.apply(thisArg,[argsArray])

thisArg:在fn函数运行时指定的this值

argsArray:传递的值 必须包含在数组里面

返回值就是函数的返回值,因为它就是调用函数

var o ={
    name: 'andy';
}

function fn(uname) {
    console.log(this);
    console.log(uname); //'ldh'
};
fn.apply(o,['ldh']);
//1.也是调用函数 也可以改变this指向
//2.但他的参数必须是数组(伪数组)
//3.apply主要应用  比如可以利用apply借助于数学内置对象求最大值
var arr = [1,66,3,99,4];
var max = Math.max.apply(Math,arr);
console.log(max); //99

3) bind()

fn.bind(thisArg,arg1, arg2,...)

参数同call()  但bind()方法不会调用函数,能改变this指向

返回由指定的this值和初始化参数改造后的原函数拷贝

var o = {    name: 'andy',}function fn(uname) {    console.log(this);    console.log(uname); //'ldh'};var f = fn.bind(o,'wbh');f(); 
// 返回的是原函数改变this之后产生的新函数
//如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this,此时用bind

//例 有一个按钮,点击之后禁用三秒 之后开启
var btn = document.querySelector('button');
btn.onclick = function(){
    this.disabled = true;  //this指向btn
    
    setTimeout(function() {
        this.disabled = false;
    }.bind(this),3000)   //这个this指向btn  使得上一行的this也指向btn
}

严格模式

严格模式可以分为 整个脚本 和个别函数的严格模式。IE10以上支持

1.开启严格模式

1)为脚本开启严格模式

在所有语句前放一个特定的语句 'use strict';

2)为某个函数开启严格模式

function fn(){
    'use strict';
//下面的代码按照严格模式执行
}

2.严格模式中的变化

  1. 变量和this规定
  • 正常模式里,如果变量没声明就赋值,默认是全局变量。严格模式必须先用var命令声明 才能使用
  • 严禁删除已经声明的变量 delete x;语法是错误的
  • 严格模式下  全局作用域中的this是undefined 而不是window
  • 严格模式下 如果构造函数不加new调用 this会报错(普通模式下不加new也可以,只不过当做普通函数,this指向全局对象)
  • 严格模式下,定时器里面的this指向的还是window
  • 严格模式下,一个函数里面不能有重名的参数,
  • 严格模式下,不允许在非函数的代码块内声明函数(比如for循环,if)

高阶函数

高阶函数是对其他函数进行操作的函数,它接受函数作为参数或者将函数作为返回值输出。

function fn(a, callback){    console.log(a);    callback && callback();}fn(1,function(){    console.log('我是最后调用的');})//接受函数作为参数

闭包

闭包指有权访问另一个函数作用域中变量的函数

被访问的局部变量所在的函数就是闭包

闭包的主要作用:延伸了变量的作用范围

function fn(){
    var num = 10;
    function fun() {  //fun访问了fn中的局部变量 num  所以fun是个闭包
        console.log(num);
    }
    return fun;
}
var f = fn();  //此时fn外面的作用域就可以通过fun访问fn的局部变量num
f();

浅拷贝和深拷贝

浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用

深拷贝拷贝多层,每一层的数据都会拷贝

var obj = {
    id:1,
    name:'andy',
    msg:{
        age:18
    }
};
var o = {};
for (var k in obj){
    o[k] = obj[k];
}
Object.assign(o,obj);//assign方法,浅拷贝的方法
console.log(o); //此时o就是obj的浅拷贝 里面的msg是同一块内存地址

深拷贝:

function deepCopy(newobj,oldobj){
    for(var k in oldobj){
        var item = oldobj[k];
        if (item instanceof Array){
            newobj[k] = [];
            deepCopy(newobj[k],item)
        } else if (item instanceof Object){
            newobj[k] = {};
            deepCopy(newobj[k],item)
        } else {
            newobj[k] = item;
        }
    }
}

ES6

1.let 

let声明的变量只能在所处于的块级作用域使用

块级作用域即一对大括号包含的区域

if (true) {
    let a = 10;
}
console.log(a); //undefined

注意:使用let声明的变量才具有块级作用域,var声明的变量不具备块级作用域

//防止循环变量变成全局变量
for(let i = 0;i<2;i++){  //let i也跟这个for的块级作用域绑定
}
console.log(i);//访问不到
//let声明每次迭代都会创建一个新变量,并以之前迭代中的同名变量的值将其初始化
//这个特性对于for-of循环和for-in循环来说也是一样的

使用let声明的变量 不存在变量提升

console.log(a); // 报错 a is not defined
let a = 20;

使用let声明的变量 具有暂时性死区特性

var num = 10;
if (true){
    console.log(num);   // 因为这个if大括号内使用了let再次声明num,所以外面的num不起作用,此时依然报错:初始化前无法访问num
    let num = 20;       //一旦在块级作用域用let声明变量,变量就会和这个块级作用域暂时绑定
}

面试题:

var arr = [];
for(let i =0;i<2;i++){
    arr[i] = function(){
        console.log(i);
    }}
arr[0]();  //0
arr[1]();  //1
//let声明每次迭代都会创建一个新变量,并以之前迭代中的同名变量的值将其初始化
//所以arr[0]这个函数所在块级作用域中i是0 arr[1]所在块级作用域中i是1

2.const

声明常量,常量就是值(内存地址)不能变化的量

  • 具有块级作用域

  • 声明const常量时必须赋初始值 

    const PI; //报错 缺少了初始值

  • 常量赋值后,值不可修改。对于基本数据类型,赋值后不能改值的大小。对于复杂数据类型,赋值后不能更换内存地址,但是可以改数据结构内部的值

    const PI = 3.14; PI = 100; //报错 不能给常量修改值

    const arr = [100,200]; arr[0] = 'a'; arr[1] = 'b'; console.log(arr); // ['a','b'] arr = ['a','b'] //修改内存地址 不允许 所以报错

let const var 区别

3. 解构赋值

ES6中允许从数组中提取值,按照对应位置,对变量赋值。对象也可以实现解构。

数组解构

let [a,b,c] = [1,2,3];  //按照一一对应的关系
console.log(a);
console.log(b);
console.log(c);

let arr = [1,2,3]
let [a,b,c] = arr;
----------------------------
//如果不是一一对应
let [foo] = []; //foo 为undefined
let [bar,foo] = [1]; //foo 为undefined

对象解构 允许使用对象的名字匹配对象的属性

let person = {name:'zhangsan', age:30};
let { name, age} = person;  //如果person中有name age 则匹配到 并把值赋值到左边

----------------------------------------------------
let {name:myName} = person; //用name匹配person里面的属性 匹配成功则把属性值赋给变量myName
console.log(myName); //'zhangsan'

4. 箭头函数

() => {}    //小括号里放形参 大括号里放函数体
const fn = () => {}
fn();

如果函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号

function sum(a,b) {
    return a + b;
}
`````````````````````````````````
const sum = (a,b) => a + b;

const result = sum(10,20);
console.log(result); // 30

如果形参只有一个,可以省略小括号

function fn (v) {
    return v;
}
````````````````````````````````
const fn = v => v;

箭头函数不绑定this 箭头函数没有自己的this关键字,如果在箭头函数中使用this,this关键字将会指向箭头函数定义位置中的this

function fn() {
    console.log(this);
    return () => {
        console.log(this)
    }
}

const obj = {name:'zhangsan'};
const resFn = fn.call(obj); //resFn就是上面那个匿名函数
resFn(); //输出的是obj这个对象  因为箭头函数中没有this,不然匿名函数是个普通函数,其this应该是window

箭头函数面试题

var age = 100;
var obj = {
    age:20,
    say:() => {
        alert(this)
    }}
obj.say(); //100;
//因为obj对象不产生作用域,所以say箭头函数定义在全局作用域下。箭头函数要换成function 就输出20

5.剩余参数

const sum = (...args) => {  //形参前加三个点,就表示接受剩余的参数,并存放在一个数组里
    let total = 0;
    args.forEach(item =>  {
        total += item;
    })
    return total;
};

sum(10,20);
sum(10,20,30);

剩余参数和解构配合使用

let students = ['wangwu','zhangsan','lisi'];
let [s1,  ...s2] = students;
console.log(s1);'wangwu'
console.log(s2); //['zhangsan','lisi']

6.Array的扩展方法

  • 扩展运算符   可以将数组拆分成以逗号分隔的参数序列

    let ary = [1,2,3]; ...ary // 1,2,3 console.log(...ary); console.log(1,2,3); //两种console.log 结果一样

应用1:合并数组 

//方法1
let ary1 = [1,2,3];
let ary2 = [4,5,6];
let ary3 = [...ary1, ...ary2]; // [1,2,3,4,5,6]

//方法2
ary1.push(...ary2);

应用2:将类数组或者可遍历对象转换成真正的数组

let oDivs = document.getElementsByTagName('div');
oDivs = [...oDivs];
  • 构造函数方法Array.from()

    let arrayLike = { '0':'a', '1':'b', '2':'c', 'length':3 //还非得是索引0,1,2,和length }; let arr2 = Array.from(arrayLike); //['a','b','c']

from方法还可以接受第二个参数,类似于数组中的map方法,用于对每个元素进行处理,将处理后的值放入返回的数组。

let arrayLike = {
    "0":1,
    "1":2,
    "length":2
}
let newAry = Array.from(aryLike, item => item *2); //[2,4]
  • 实例方法find()

用于找出第一个符合条件的数组成员,如果没有,则返回undefined

find(function(currentValue,index,arr))

find方法给数组中每一个元素调用测试函数,测试函数返回true时,find()返回符合条件的元素,之后的值不会再调用执行函数

let arr = [{
    id:1,
    name:'张三'
},{
    id:2,
    name:'李四'
}];
let target = arr.find(item => item.id == 2);//target是一个对象
  • 实例方法findIndex()

用于找到第一个符合条件的数组成员的位置,如果没有 返回-1

let arr = [1,5,10,15];
let index = arr.findIndex(value => value > 9);
console.log(index); // 2
  • 实例方法includes()

表示某个数组是否包含给定的值,返回布尔值

[1,2,3].includes(2); //true
[1,2,3].includes(4); //false

7.String 的扩展方法

  • 模板字符串

ES6中新增的创建字符串的方式,使用反引号定义。

模板字符串可以解析变量  利用${ }

let name = `张三`;
let sayHello = `hello, my name is ${name}`;

模板字符串中可以换行

let result = {
    name:'zhangsan';
    age:20;
    sex:'男';
}
let html = ` <div>
    <span>${result.name}</span>
    <span>${result.age}</span>
    <span>${result.sex}</span></div>`;

模板字符串中可以调用函数

const sayHello = function () {
    return '恭喜RNG在2021年MSI中夺得冠军!';
};

let greet = `${sayHello()}`;
console.log(greet);
  • startsWith()  endsWith()

表示参数字符串是否在原字符串的头部、尾部  返回布尔值

let str = 'Hello world!';
str.startsWith('Hello'); //true
str.endsWith('!');  //true
  • 实例方法repeat()

repeat()方法表示将原字符串重复n次,返回一个新字符串

'x'.repeat(3)  //"xxx"
'hello'.repeat(2)  //"hellohello"

8.set数据结构

ES6中新的数据结构set,类似于数组,但是成员的值是唯一的,不重复

set本身是一个构造函数,用来生成set数据结构

const s = new Set();
console.log(s.size); // 0

const set = new Set([1,2,3,4,4]); // {1,2,3,4}

//数组去重
const s2 = new Set(['a','a','b','b']);
const ary = [...s2];
console.log(ary); // ['a','b']

set实例方法:

遍历set

跟数组一样用forEach

s.forEach(value => console.log(value))