js高级 es6 箭头函数 函数参数默认值 扩展运算符 Set 原型链继承 函数的四种调用模式

131 阅读11分钟

“携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

js高级

箭头函数

函数的简写

// 箭头函数是匿名函数,一般做为参数传递
// let test = function (a,b){
//     let sum = a + b 
//     return sum
// }
// let test = (参数) => {函数体}
// 几个小细节
// 1.如果函数体只有一句,那么可以省略{},同时默认会返回函数体的结果,不能写return
// 2.如果只有一个参数,那么可以省略()
// 3.如果没有参数,()也不能省略
// let test = (a,b) =>  a + b    // 不能有大括号
let test = a =>  a + 10 

let res = test(100)
console.log(res)

函数参数的默认值

定义函数的同时,可以给形参一个默认值

// 定义函数的同时,可以给形参一个默认值
function show(msg = "大家一起快活呀") {
  console.log(msg);
}

show();// 打印 大家一起快活呀
show("搞笑不");// 打印 搞笑不

提供更加方便获取数组中元素或者对象中属性的写法

获取数组中的元素

  const [a, b, c, d] = [1, 2, 3, 4];
  console.log(a, b, c, d);// 1,2,3,4

元素交互顺序

let a = 1111;
let b = 2222;
[b, a] = [a, b];
console.log(a, b);// 2222 1111

获取对象中的属性(重点)

  const obj = {
    name: "悟空",
    skill: "72变",
    say() { }
  }
  const { name, skill,say } = obj;
  console.log(name, skill,say);// 悟空 72变 function(){}

对象简写

在定义对象的时候,如果属性名和变量名相同,那么可以实现简写

const name = "悟空";
const skill = "72变";
const say = function () { }
const obj = {
  name, skill, say
}
console.log(obj);// {name:"悟空",skill:"72变",say:function(){}}

对象的方法也可以简写

const obj = {
  say() {
    console.log(this);
  }
}
  • 12-对象简写-属性
  • 13-对象简写-方法

拓展运算符 || 剩余运算符

通过 ...符号来获取剩下的参数

函数内获取

function show(a, ...all) {		// 只能放最后
  console.log(a);
  console.log(all);
}
show(1);// 1 []
show(1, 2, 3);// 1 [2,3]

数组内获取

const [a, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 1
console.log(rest);// [2, 3, 4, 5]

对象内获取

const obj={
  name:"悟空",
  skill:"72变",
  say(){}
}

const {name,...others}=obj;
console.log(name); // 悟空
console.log(others); // {skill: "72变", say: ƒ}

常用数组方法

方法描述
every()检测数值元素的每个元素是否都符合条件。
filter()检测数值元素,并返回符合条件所有元素的数组。
find()返回符合传入测试(函数)条件的数组元素。
findIndex()返回符合传入测试(函数)条件的数组元素索引。
forEach()数组每个元素都执行一次回调函数。
includes()判断一个数组是否包含一个指定的值。
indexOf()搜索数组中的元素,并返回它所在的位置。
isArray()判断对象是否为数组。
join()把数组的所有元素放入一个字符串。
map()通过指定函数处理数组的每个元素,并返回处理后的数组。
reduce()将数组元素计算为一个值(从左到右)。
reverse()反转数组的元素顺序。
some()检测数组元素中是否有元素符合指定条件。
sort()对数组的元素进行排序。

Set

永远不会有重复元素的对象

可以理解为不重复的数组

const set = new Set([1, 5, 3, 4]);
set.add(5);
set.add(2);
console.log(set);

Set对象转为数组

const set = new Set([1, 5, 3, 4]);
set.add(5);
set.add(2);
console.log(set);

const arr = [...set];// 将set对象转数组
console.log(arr);

面向对象

一种编程行业通用的写项目级的代码的思维,引导我们如何编写高质量的代码,万物皆对象 - 看待事物的角度,(属性:数据,行为:动作(方法))。代码特点是封装和继承

对象在内存中的示意图

请添加图片描述

创建对象的几种方式

字面量

  • 简单粗暴
  • 不适合创建多个同样类型的对象的场景

const obj ={ name:"悟空",height:100,age:5000};

工厂函数

  1. 容易理解

  2. 失去血缘关系,无法简单分辨对象的特征

  3. 后期无法实现继承

请添加图片描述

 function createPerson(name, age, height) {
  return {
    name: name,
    age: age,
    height: height
  }
}

function createStudent(name, age, height, grade) {
  return {
    name1: name,
    age1: age,
    height1: height,
    grade: grade
  }
}

// 这个不是工厂模式
function createStudent2(name, age, height, grade) {
  this.name = name;
  this.age = age;
  this.height = height;
  this.grade = grade;
}
const obj1 = createPerson("八戒", 18, 500);
const obj2 = createStudent("悟能", 83, 231, 3);
const obj3 = new createStudent2("悟能", 83, 231, 3);

console.log(obj1);
console.log(obj2);
console.log(obj3);

:star:构造函数

  1. 可以方便的创建对象
  2. 拥有血缘关系
  3. 还有后续更多的优势
// 1 声明函数 
function createStudent(name, age) {
  // 2 通过 this 赋值
  this.name = name;
  this.age = age;
}

// 3 通过 new 来创建对象
const obj = new createStudent("悟能", 83);

console.log(obj);

小结: 笔试题比较常见

构造函数的工作原理:

1. 创建一个空对象,把它的地址赋值给this
2. 把一些属性和方法添加到this3.this的proto指向 构造函数的prototype
4.this指向实例

构造函数的弊端

同一个 say 方法占据了两份内存

  function createStudent(name, age) {
  this.name = name;
  this.age = age;
  this.say = function () {
    console.log(this.name);
  }
}

const obj = new createStudent("悟能", 83);
const obj1 = new createStudent("悟能1", 84);

console.log(obj.say === obj1.say); // false  不是同一say方法 浪费了内存

图示

请添加图片描述

提取同一个 say 方法

  1. 解决了浪费内存的弊端
  2. 但是造成了 污染全局变量 的问题
// 提前将say 声明好
function say() {  // 污染全局变量
  console.log(this.name);
}
function createStudent(name, age) {
  this.name = name;
  this.age = age;
  this.say = say
}

const obj = new createStudent("悟能", 83);
const obj1 = new createStudent("悟能1", 84);

console.log(obj.say === obj1.say); // true

图示

请添加图片描述

原型模式

在构造函数的原型上 存放函数

  1. 解决了同一个 say 浪费 内存的问题
  2. 解决了污染全局变量的问题
function createStudent(name, age) {
  this.name = name;
  this.age = age;
}
// 将刚才的全局函数say 直接挂载到 构造函数的原型上 即可
// prototype 是个对象 每一个构造函数都会内置有的. 我们称之为原型
createStudent.prototype.say = function () {
  console.log(this.name);
}

const obj = new createStudent("悟能", 83);
const obj1 = new createStudent("悟能1", 84);

console.log(obj.say === obj1.say); // true

解释

  • 原型的单词是 prototype, 原型的这个名字是行业内共同认可的名字。
  • 原型本质是一个对象,理解为 JavaScript 自动帮我们添加的
  • 原型是 JavaScript 自动帮我们在定义构造函数的时候添加的
  • 所有构造函数的实例,共享一个原型
  • 原型上一般是挂载函数

请添加图片描述

原型链继承

利用代码的能力实现 面向对象的特性 封装继承

初体验

  1. 子类strudent 继承了父类 Person的属性
// 父类
function Person(name, height) {
  this.name = name;
  this.height = height;
}

Person.prototype.say = function () {
  console.log(this.name);
  console.log(this.height);
}

// 子类
function Student(grade, name, height) {
  // 借用了父类的构造函数,完成对自己的赋值
  Person.call(this, name, height)
  this.grade = grade;
}

// 赋值了父类原型上的所有的 属性和方法
Student.prototype ={... Person.prototype};
// 修改之类的指向
Student.prototype.constructor = Student;

// 创建子类的实例
const stu = new Student("一年", "周星星", 170);
stu.say();

需求

  1. 有一个负责创建元素的构造函数 A
  2. 有一个负责创建图片的构造函数 B
  3. 构造函数 B 可以使用 构造函数 A 的原型上的所有的功能 实现继承

效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V8NedTbg-1660869785753)(js高级.assets/4bb7e03c-172b-4f9d-9fdc-a34057e5fd86.png)]

代码

// 1 负责创建元素  
function Element(nodeName, text) {
  const node = document.createElement(nodeName);
  node.classList.add("element")
  node.innerText = text;
  this.node = node;
}

// 2 原型上挂载 共用的方法
Element.prototype.appendTo = function (selector) {
  const parent = document.querySelector(selector);
  parent.appendChild(this.node);
}

// 3 创建一个实例
const div = new Element("div", "做人开心就好");
// 4 追加到父元素上
div.appendTo("section");

// 5 一个新的构造函数 用来创建图片
function ElementImg(src) {
  // 6 借用了 1 中的构造函数,并且把参数传递了进去
  Element.call(this, "img", "");
  // 7 图片设置路径
  this.node.src = src;
}

// 8 继承了 父亲的构造函数上的原型上的所有 函数
ElementImg.prototype=Element.prototype;			// 修改来原型,也就修改了构造函数
// 9 重新将 constructor 的指向改回来
ElementImg.prototype.constructor=ElementImg;

// 10 创建一个图片实例:注意,实例化代码10不能在8和9之前
const img = new ElementImg("images/01.png");

// 11 创建到父元素上
img.appendTo("section");

es6 class(重点)

es6的class 的出现 基本上可以替代了es5的构造函数和原型,使之代码结构上更加简洁。

关键字

  1. class
  2. 属性
  3. 方法
  4. 继承 extends
  5. 构造函数 constructor
  6. 方法重写 override:子类方法覆盖父类,super.父类方法()
  7. 父类的构造函数 super :子类有构造方法且使用this前,必须使用super()

完整代码体验

class Person {
  // 构造方法
  constructor(name) {
    // 属性
    this.name = name;
  }
  // 方法
  say() {
    console.log(this.name);
  }
}
// 继承
class Student extends Person{
  constructor(name,height){
    // console.log(this);			// 语法错误:必须先调用super()才能使用this
    super(name);
    this.height=height;
  }
}

const s1=new Student("八神",180);
s1.say();							// 八神

class Saler extends Person{
    constructor(name,age){
        super(name);
        this.age = age;
    }
    // 覆盖(重写)
    say(){
        // 访问父类方法
        super.say();				// 马云
        console.log(this.age);
    }
}

const s2 = new Saler('马云',50);
s2.say();							// 50

函数的四种调用模式

根据函数内部this的指向不同,可以将函数的调用模式分成4种

  1. 函数调用模式
  2. 方法调用模式
  3. 构造函数调用模式
  4. 上下文调用模式(借用方法模式)

函数调用模式

如果一个函数不是一个对象的属性时,就是被当做一个函数来进行调用的。此时this指向了window

function fn(){
  console.log(this);// 指向window 
}
fn();

方法调用模式

当一个函数被保存为对象的一个属性时,我们称之为一个方法。当一个方法被调用时,this被绑定到当前对象

const obj = {
  sayHi:function(){
    console.log(this);//在方法调用模式中,this指向调用当前方法的对象。
  }
}
obj.sayHi();

构造函数调用模式

如果函数是通过new关键字进行调用的,此时this被绑定到创建出来的新对象上

function Person(){
  console.log(this);
}
Person();//this指向什么?
var p = new Person();//this指向什么?

:star:方法借用模式

也叫上下文模式,分为 apply 与 call

call

call方法可以调用一个函数,并且可以指定这个函数的this指向

const RichWumon = {
  name: "富婆",
  say: function () {
    console.log(this.name, " 我要重金求子");
  }
}

const obj = {
  name: "屌丝"
}

RichWumon.say();			// 富婆
RichWumon.say.call(obj);	// 屌丝

call应用

  1. 将伪数组转成数组
let divs = document.querySelectorAll('div');
// let divs = document.body.children;
console.log(divs);

function change(nodelist) {
    console.log(Object.prototype.toString.call(nodelist));
    return Array.prototype.slice.call(nodelist);

}

apply

就是apply()方法接受的是一个包含多个参数的数组。而call()方法接受的是若干个参数的列表

可以利用apply 将 刚才的call 的代码修改一下

const RichWumon = {
  name: "富婆",
  say: function () {
    console.log(this.name, " 我要重金求子");
  }
}

const obj = {
  name: "屌丝"
}

RichWumon.say();			// 富婆
RichWumon.say.apply(obj);	// 屌丝

apply应用

1.简化log方法

// 简化log方法
function log() {
    // 不需要改变this
    console.log.apply(console, arguments);
}

bind方法

bind()方法创建一个新的函数, 可以绑定新的函数的this指向

var name = '张三';
function Fn(){
    this.age = 1;
    
    console.log(this.name + this.age);
}

Fn();			// 张三 1

// 返回值:新的函数
// 参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
let obj = {
    name:'小强',
}
const newFn = Fn.bind(obj);
newFn();		// 小强 1

  console.log(this.name, " 我要重金求子");
  }
}

const obj = {
  name: "屌丝"
}

RichWumon.say();			// 富婆
RichWumon.say.apply(obj);	// 屌丝

apply应用

1.简化log方法

// 简化log方法
function log() {
    // 不需要改变this
    console.log.apply(console, arguments);
}

bind方法

bind() 方法创建一个新的函数, 可以绑定新的函数的this指向

 var name = '张三';
 function Fn(){
     this.age = 1;
     
     console.log(this.name + this.age);
 }
 ​
 Fn();           // 张三 1
 ​
 // 返回值:新的函数
 // 参数:新函数的this指向,当绑定了新函数的this指向后,无论使用何种调用模式,this都不会改变。
 let obj = {
     name:'小强',
 }
 const newFn = Fn.bind(obj);
 newFn();        // 小强 1

call、apply和bind的区别

 apply、call和bind 都可以修改this的指向。
 ​
 1. call和apply方法都可以调用函数
 ​
 2. call方法可以有多个参数,除了第一个参数,其他参数作为实参传递给函数
     apply方法最多有2个参数,第二个参数是个数组或伪数组,数组里面的每一项作为实参传递给函数
 ​
 3. bind方法不能调用函数,它会创建一个副本函数,并且绑定新函数的this指向。
 ​
 应用场景:
   1 call 经常做继承
   
   2 apply 经常跟数组有关系,比如借助数学对象实现数组最大值最小值
     // 如果一个数组我们已知里面全都是数字,想要知道最大的那个数,由于 Array 没有 max 方法,Math 对象上有
       let newArr = [12, 5, 6, 3, 4]
       let min = Math.min.apply(this, newArr)  //求最大值
       let max = Math.max.apply(this, newArr)  //求最小值
     // 另一种写法:
       const numbers = [9, 4, 7, 1];
       Math.min(...numbers); // 1
       Math.max(...numbers); // 9
   3 bind 不调用函数,但还是改变了this 的指向,比如改变定时器的this指向