Js高级笔记

箭头函数

优点

(1)简洁的语法、
(2)隐式返回,如 下面的代码可以去掉return,代码移到一行,减少代码量numbers.map((number)=>number*2)
(3)解决了this的指向问题,原生的写法this指向的是调用者,箭头函数this绑定的是定义时的那个对象。如果有对象嵌套的情况,则this绑定到最近的一层对象上

(4) 箭头函数没有 prototype 属性,不能进行 new 实例化,亦不能通过 call、apply 等绑定 this;

(5) 在定义类的方法时,箭头函数不需要在 constructor 中绑定 this。

缺点

(1)做为构造函数的时候不能使用箭头函数
(2)真正需要this的时候如给元素绑定click事件的 时候,执行的回调函数不能使用箭头函数。
(3)我们需要使用arguments对象的时候不能使箭头函数。箭头函数中没有arguments对象。
(4)对象的方法也不可以使用箭头函数。

(5) 需要this指向调用对象的时候也不能用箭头函数。

箭头函数是匿名函数,一般做为参数传递

  1. 没有形参、没有返回值
    1. const func = () => {
    2. console.log('执行业务');
    3. };
  1. 没有形参、没有返回值、业务只有一行代码 花括号都可以省略
    如果没有参数,()也不能省略
    1. const func = () => console.log('执行业务');
  1. 只有一个形参、没有返回值、业务只有一行代码(括号能省略) ```javascript 1.const func = num => console.log(num + 1);

2.const func = (num) => console.log(num + 1);

  1. 4. 两个或者多个参数(括号不能省略)、没有返回值、业务只有一行代码
  2. ```javascript
  3. const func = (a, b) => console.log(a + b);
  1. 没有形参,有返回值 业务两行代码
    1. const func = () => {
    2. let a = 100;
    3. return a + 100;
    4. };
  1. 没有形参、有返回值,业务一行代码
    1. const func7 = () => {
    2. return 100 + 200;
    3. };
  1. 没有形参、有返回值,业务一行代码 等价上述6的写法
    1. const func7 = () => 100 + 200; // 相等于 return 100+200
  1. 例子
    1. const button = document.querySelector('button');
    2. button.addEventListener('click', () => {
    3. console.log(123321);
    4. });

面向对象

面向过程和面向对象

面向过程

概述:
  1. 面向过程就是分析出实现需求所需要的步骤,通过函数一步步实现这些步骤,然后依次调用
  • 优点:性能比面向对象好,因为类调用时需要实例化,开销比较大,比较消耗资源。
    缺点:不易维护、不易复用、不易扩展。

面向对象

概述:
  1. 面向对象是把整个需求按照特点、功能划分,将这些存在共性的部分封装成对象,创建了对象不是为了完成某一个步骤,而是描述某个事物在解决问题的步骤中的行为。
  2. 一种编程行业通用的写项目级的代码的思维,引导我们如何编写高质量的代码,万物皆对象 -看待事物的角度,(属性:数据,行为:动作(方法))。代码特点是封装和继承
  3. 面向对象经常用到的场合:需要封装和复用性的场合(组件思维)。
  • 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护 。
  • 缺点:性能比面向过程差

面向对象的本质

定义不同的类,让类的实例工作

字面量-创建对象

  1. const obj = {}

工厂函数

无法实现继承

  1. function createPerson(name) {
  2. return {name:name}
  3. }

构造函数

  1. 构造函数的首字母大写 - 行内编码规范
  2. 构造函数内不要写 return
    1. function Createobj() {
    2. this.name = 'jack';
    3. //返回一个基本数据类型
    4. return undefined
    5. }
    6. 构造函数内不要写 return
    7. => 当你return 一个基本数据类型的时候,写了白写
    8. => 当你return 一个复杂数据类型的时候,构造函数白写
  1. 构造函数想要解决 性能问题
    1. 一定会把方法-函数写在构造函数的外面
    2. 再通过this.say 指向外部的函数

构造函数内的this

  1. 因为函数调用和new关键字连用,this会指向自动创建出来的那个对象
  2. 又因为这个对象会被自动返回,所以不需要return
  3. 构造函数自动创建对象的过程,叫做实例化的过程,管构造函数创建出来的对象,叫做实例对象
  4. 构造函数内的this指向当前实例

工作原理

  1. 开辟空间
  2. 将新的创建的对象指向构造函数中的this
  3. 为对象赋值
  4. 将创建好的对象的地址返回
  1. function say() {
  2. console.log('这个是say方法');
  3. }
  4. function CreatePerson(name) {
  5. this.name = name; // 创建了name属性
  6. // this.say = function () {
  7. // console.log('这个是say方法');
  8. // };
  9. this.say = say; // say函数引用类型, 构造函数中的say 其实和外面的say内存地址一致的 同一个say方法
  10. }
  11. const obj1 = new CreatePerson('悟空');
  12. const obj2 = new CreatePerson('八戒');
  13. // obj1.say();
  14. // obj2.say();
  15. // 两个say的判断比较 是false 说明 两个say是在不同的内存空间上
  16. // 两个say 占用了两个内存空间
  17. // console.log(obj1.say === obj2.say); // false
  18. console.log(obj1.say === obj2.say); // true
  19. // 对于基本类型来说,= 就是复制了一份值
  20. // let num=100;
  21. // let num2=num;// 复制值 num和 num2占两给内存 各自不影响
  22. // num2=1000;
  23. // console.log(num);
  24. // 对于引用类似 = 其实把地址拷贝了一份给新的对象 两个数据 公用一份数据
  25. // let person1 = { name: '悟空' }; // person1 指向了 一个地址 0x1111 存放悟空
  26. // let person2 = person1; // person2也指向了person1的地址 0x1111 person1和person2 通用一个数据
  27. // 修改了person2 person1也会发生改变
  28. // person2.name = '八戒';
  29. // console.log(person1);
  30. // person2 和person1 共同指向了同一个地址
  31. // console.log(person1 === person2); // true
  32. // let o={};// 开辟了一个内存
  33. // let o2={};// 开辟了另外一个内存
  34. // console.log(o===o2);// false 两个不同的内存地址

构造函数的全局污染

  1. // 构造函数的 方法 都会通过类似的这种方式 来实现 多个实例的方法共享
  2. function say() {
  3. console.log('你好');
  4. }
  5. function CreatePerson(name) {
  6. this.nickname = name;
  7. this.say = say;
  8. }
  9. // function say() {
  10. // console.log("学生你好");
  11. // }
  12. // function createStudent(params) {
  13. // }
  14. const obj1 = new CreatePerson('悟空');
  15. const obj2 = new CreatePerson('八戒');
  16. console.log(obj1);
  17. console.log(obj2);
  18. console.log(obj1.say === obj2.say); // true 这个代码是合理的 优化过
  19. console.log(obj1.say === obj2.say); // false 不够好 性能不够好 两个say占据了两个内存
  20. // 函数函数 方法提取出去 这套代码
  21. // 优点: 方便代码维护、也解决了性能 obj1.say === obj2.say
  22. // 缺点: 代码不够优雅 污染了全局变量 以后不能写 say方法,很容易就覆盖()

构造函数的弊端

  1. 同一个 同样的方法会占据两份内存
    解决:
    1. 提取同一个同样的方法
      但提取同一个同样的方法虽然解决了浪费内存的弊端,又造成了 污染全局变量 的问题
      解决:
      原型上存放函数

原型对象

在构造函数里面

  1. 每一个函数天生在自带一个属性prototype,是一个对象数据类型
  2. 构造函数也是一个函数,所以构造函数也有prototype,
    这个天生自带的prototype内有一个constrictor的属性。
    =>表明我是谁的构造函数
    =>指向自己的构造函数
  3. 原型是一个对象,表明可以向里面添加一些成员

在实例对象里面

  1. 每一个对象天生自带一个属性, proto ,指向所属构造函数的prototype
  2. 实例对象也是一个对象,所以实例对象也有 proto

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

优点

  1. 解决了同一个 say 浪费 内存的问题
  2. 解决了污染全局变量的问题

解释

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

原型链

原型链的终点

  1. function People(name, age) {
  2. this.name = name;
  3. this.age = age;
  4. }
  5. const xiaoming = new People('小明', 18);
  6. console.log(xiaoming.__proto__.__proto__ === Object.prototype); //true
  7. console.log(Object.prototype.__proto__); //null
  8. console.log(Object.prototype.hasOwnProperty('hasOwnProperty')); //true
  9. console.log(Object.prototype.hasOwnProperty('toString')); //true

image.png

数组的原型链
  1. let arr = [12, 23, 15, 32];
  2. console.log(arr.__proto__ === Array.prototype); //true
  3. console.log(arr.__proto__.__proto__ === Object.prototype); //true
  4. console.log(Array.prototype.hasOwnProperty('push')); //true
  5. console.log(Array.prototype.hasOwnProperty('splice')); //true

image.png

原型链继承

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

  1. function createStudent(name, age) {
  2. this.name = name;
  3. this.age = age;
  4. }
  5. // 将刚才的全局函数say 直接挂载到 构造函数的原型上 即可
  6. // prototype 是个对象 每一个构造函数都会内置有的. 我们称之为原型
  7. createStudent.prototype.say = function () {
  8. console.log(this.name);
  9. }
  10. const obj = new createStudent("悟能", 83);
  11. const obj1 = new createStudent("悟能1", 84);
  12. console.log(obj.say === obj1.say); // true
  1. // 创建图片
  2. function MyImg(src) {
  3. const img = document.createElement('img');
  4. img.src = src;
  5. document.body.appendChild(img);
  6. this.dom = img;
  7. }
  8. MyImg.prototype.scale = function() {
  9. this.dom.classList.add("scale");
  10. }
  11. const imgModel1 = new MyImg('./images/gotop.png');
  12. const btn1 = document.querySelector(".btn1");
  13. btn1.addEventListener("click", function() {
  14. imgModel1.scale();
  15. })
  16. // 复杂业务 面向对象技术需要(构造函数、this、原型对象)
  1. // 封装 代码 实现以下的功能
  2. // 1 父亲 Element
  3. // 1 属性 dom this.dom
  4. // 2 行为 实例.append(父元素标签的选择器)
  5. // 2 儿子1 ElementDouble div
  6. // 1 继承父亲 Element
  7. // 属性 dom
  8. // 行为 append
  9. // 3 儿子2 ElementSingle img
  10. // 属性 dom
  11. // 行为 append
  12. // 4 当执行以下代码时 出现对应效果
  13. // 1 const divModel = new ElementDouble("div","div内的文字")
  14. // 2 divModel.append("body") ; body标签可以出现 一个 div
  15. // 1 const imgModel = new ElementSingle("img","图片地址")
  16. // 2 imgModel.append("body"); body标签可以出现 一个图片
  17. // 父亲
  18. function Element(tagName) {
  19. const dom = document.createElement(tagName);
  20. this.dom = dom;
  21. }
  22. // 父亲
  23. Element.prototype.append = function(parentSelector) {
  24. document.querySelector(parentSelector).appendChild(this.dom);
  25. };
  26. // 儿子1
  27. function ElementDouble(tagName, content) {
  28. Element.call(this, tagName); // 继承 - 父亲的属性
  29. this.dom.innerText = content;
  30. }
  31. // 去继承父亲的行为
  32. ElementDouble.prototype.append = Element.prototype.append;
  33. // 儿子2
  34. function ElementSingle(tagName, src) {
  35. Element.call(this, tagName);
  36. this.dom.src = src;
  37. }
  38. ElementSingle.prototype.append = Element.prototype.append;
  39. const divModel = new ElementDouble('div', '这个是div');
  40. divModel.append('body');
  41. const imgModel = new ElementSingle('img', './images/b_01.jpg');
  42. imgModel.append('div');
  43. // 如果代码重复实现了 很有可能就是我们要封装的时候
  44. // 以前的封装 仅仅是封装一个小小的函数而已
  45. // 现在的封装, 面相对象的思维来封装
  46. // 封装 属性 父亲
  47. // 封装 方法 父亲
  48. // 两个儿子的代码 有重复部分
  49. // 创建标签重复 this.dom=dom append方法也重复
  50. // 儿子1 有要什么功能
  51. // 先复用父亲的属性和方法

使用结论

  1. 普通的属性写在构造函数内
  2. 行为方法写在原型上prototype

包装类

  1. 很多语言都有“包装类”的设计,包装类的目的就是为了让基本数据类型值可以从它们的构造函数的prototype上获得方法
  2. Number()、String()、和Boolean()分别是数字、字符串、布尔值的“包装类”.
  1. let a = new Number(123);
  2. console.log(a); // Number {123}
  3. console.log(typeof a); //Object
  4. let b = new String('牛奶');
  5. console.log(b); // String {"牛奶"}
  6. console.log(typeof b); // Object
  7. let c = new Boolean(true);
  8. console.log(c); // Boolean {true}
  9. console.log(typeof c); // Object
  10. console.log(1 + a); // 124
  11. console.log(b.slice(0, 1)); //牛
  12. console.log(b.slice(0, 2)); //牛奶
  13. let d = 1;
  14. console.log(d.__proto__ == Number.prototype); //true

包装类总结

  1. Number()、String()、和Boolean()的实例都是object类型,它们的PrimitiveValue属性存储它们的本身的值
    Js高级笔记 - 图3
  2. new出来的基本类型值可以正常参与运算

Es6 Class

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

关键字

  1. class
  2. 属性
  3. 方法
  4. 继承 extends
  5. 构造函数 constructor
  6. 方法重写 override:子类方法覆盖父类,super.父类方法()
  7. 父类的构造函数 super :子类有构造方法且使用this前,必须使用super()
  1. // 方法 constructor 在es6 构造函数
  2. // constructor 会在 new Person的时候触发
  3. class Person {
  4. constructor(name) {
  5. this.name = name;
  6. }
  7. say() {
  8. console.log(this.name + '你好');
  9. }
  10. }
  11. const student = new Person('霉霉');
  12. student.say()
  1. class Person {
  2. // 性能最好 推荐方式!!
  3. say1() {
  4. console.log('say1');
  5. }
  6. say2 = function() {
  7. console.log('s2');
  8. };
  9. say3 = () => {
  10. console.log('s3');
  11. };
  12. }
  13. const p1 = new Person();
  14. const p2 = new Person();
  15. console.log(p1.say1 === p2.say1); // true p1 和 p2 say1方法是同一个-节省内存
  16. console.log(p1.say2 === p2.say2); // false p1 和 p2 say2方法不是同一个-性能不好
  17. console.log(p1.say3 === p2.say3); // false p1 和 p2 say3方法不是同一个-性能不好
  1. // 使用es6的class 实现以下功能
  2. // 1 父类 Element
  3. // 1 属性 this.dom
  4. // 2 方法 append
  5. // 2 定义两个子类 都要继承父亲的属性和方法
  6. // 1 ElementDouble
  7. // 2 ElementSingle
  8. // 3 然后 通过编写代码 把以下程序运行起来
  9. // 父亲
  10. class Element {
  11. constructor(tagName) {
  12. const dom = document.createElement(tagName)
  13. this.dom = dom;
  14. }
  15. append(parentSelector) {
  16. document.querySelector(parentSelector).appendChild(this.dom);
  17. }
  18. }
  19. // 儿子1
  20. // 继承父亲的方法
  21. class ElementDouble extends Element {
  22. constructor(tagName, content) {
  23. super(tagName);
  24. this.dom.innerText = content
  25. }
  26. }
  27. // 儿子2
  28. class ElementSingle extends Element {
  29. constructor(tagName, src) {
  30. super(tagName);
  31. this.dom.src = src
  32. }
  33. }
  34. const divModel = new ElementDouble('div', '这个是div');
  35. divModel.append('body');
  36. const imgModel = new ElementSingle('img', './images/b_01.jpg');
  37. imgModel.append('div');

Es6和箭头函数

  1. // es6 属性的定义 写法有两种
  2. // 1 直接在构造函数内 constructor this.name=name
  3. // 2 可以写在 大括号内
  4. // 3 方法 三种写法
  5. class Person {
  6. // color = 'yellow';
  7. // height = 180;
  8. // weight = 200;
  9. constructor(name) {
  10. this.name = name;
  11. // this.color = 'yellow';
  12. // this.height = 180;
  13. // this.weight = 200;
  14. }
  15. // 写法一
  16. // say() {
  17. // console.log('say 方法被调用了 ' + this.name);
  18. // }
  19. // 写法二
  20. // say = function () {
  21. // console.log('say 方法被调用了 ' + this.name);
  22. // };
  23. // // 写法三
  24. say = () => {
  25. // 箭头函数中的this , 绝大部分指向 window
  26. // 例外 用在 es6的class 充当 方法 this 指向 p1 实例
  27. console.log('say 方法被调用了 ' + this.name);
  28. };
  29. }
  30. const p1 = new Person('悟空');
  31. p1.say();
  32. // console.log(p1);

Class继承

  1. class Person {
  2. constructor(name) {
  3. this.name = name;
  4. }
  5. say() {
  6. console.log('say方法我调用啦 ' + this.name);
  7. }
  8. fly() {
  9. console.log('父亲的起飞');
  10. }
  11. }
  12. // 表示学生要继承父亲
  13. // extends 直接就实现了继承父亲的方法
  14. class Student extends Person {
  15. //
  16. constructor(name, color) {
  17. super(name); // 父亲的构造函数 =es5 Person.call(this,name);
  18. this.color = color;
  19. }
  20. // fly(){
  21. // console.log("儿子 起飞");
  22. // }
  23. // fly = null; // 用儿子的新的null 去覆盖父亲的fly没有父亲的fly
  24. }
  25. const s1 = new Student('学生', 'red');
  26. s1.say();
  27. s1.fly();
  28. /*
  29. Must call super constructor in derived class before accessing 'this' or returning from derived constructor
  30. 1 如果你写了 extends 而且还写了 constructor 那你必须要在 constructor 调用了方法 super();
  31. 2 如果你只写了 extends 但是你没有写constructor 不需要管super
  32. 继承要继承父亲的属性和父亲的行为
  33. 1 只实现了继承父亲的行为 还没有实现继承父亲的属性 (super 表示可以继承父亲的属性)
  34. */

函数参数默认值

  1. 定义函数的同时,可以给形参一个默认值
  2. 形参有值输出值,没有值默认值
  1. // es6 函数参数默认值
  2. // 你好 默认值
  3. function show(msg = '你好', str = "你我都好") {
  4. console.log(msg, str);
  5. }
  6. show(); // 没有传递参数 输出默认值你好
  7. show('大家好'); // 输出 大家好
  8. show('大家好', "世界美好"); // 输出 大家好

对象简写

  1. 如果变量的名字和属性的名字 一致的话,对象可以简写
  1. const obj = {
  2. // 属性名 和 属性值
  3. // username: 123,
  4. };
  5. const username = 123;
  6. const color = 'red';
  7. const say = function() {};
  8. function show() {}
  9. const obj = {
  10. username, // username:username
  11. color, // color : color
  12. say,
  13. show,
  14. height: 100,
  15. };
  16. obj.height = 200;
  17. // 对象中方法的简写
  18. const person = {
  19. show: function() {
  20. console.log("show");
  21. }, // 常规写法
  22. // es6 关于 方法简写
  23. say() {
  24. console.log("say");
  25. }
  26. }
  27. person.show();
  28. person.say();
  29. // 小结:
  30. // 1 变量名如果和你的对象的属性名一致 可以简写
  31. let username='悟空'
  32. const obj = { username }
  33. // 2 对象的方法 简写
  34. const obj ={
  35. say(){ // 简写的方法
  36. }
  37. }

解构

  1. 提供更加方便获取数组中元素或者对象中属性的写法
  2. 函数可以返回多个值
  3. 函数可以定义参数,和传入参数的顺序改变。参数可以带默认值

解构赋值

含义:解析某一数据的结构,将我们想要的东西提取出来,赋值给变量或者常量。

  1. // 索引值相同的完成赋值
  2. const [a, b, c] = [1, 2, 3];
  3. console.log(a, b, c); // 1 2 3
  4. // 不取的,可以直接用逗号跳过
  5. const [e, [, , f], g] = [1, [2, 4, 5], 3];
  6. console.log(e, f, g); // 1 5 3

解构赋值的默认值

  1. // 默认值的基本用法
  2. const [a, b] = []; //undefined undefined
  3. const [a, b] = [undefined, undefined]; // undefined undefined
  4. const [a = 1, b = 2] = []; // 1 2
  5. console.log(a, b);
  6. // 默认值的生效条件, 只有当一个数组成员严格等于( === ) undefined时, 对应的默认值才生效
  7. const [a = 1, b = 2] = [3, 0]; //3 0
  8. const [a = 1, b = 2] = [3, null]; //3 null
  9. const [a = 1, b = 2] = [3]; // 3 2
  10. console.log(a, b);
  11. // 默认值表达式
  12. // 如果默认值是表达式,默认值是惰性求值的
  13. const func = () => {
  14. console.log('我被执行了');
  15. return 2
  16. }
  17. // const [x = func()] = [1]; // 1
  18. const [x = func()] = []; // 我被执行了
  19. console.log(x); // 2

数组解构

  1. 分析某一数据的结构,将想要的提取出来,赋值给变量和常量
  2. 模式(匹配)解构
    1. [] = [1,2,3];
  1. 索引值相同的完成赋值
    1. const [a,b,c] = [1,2,3];
  1. 不取的用逗号跳过
    1. const [e, [, , f], g] = [1, [2, 4, 5], 3];

数组解构赋值的原理
  1. // 索引值相同的完成赋值
  2. const [a, b, c] = [1, 2, 3];
  3. console.log(a, b, c); // 1 2 3
  4. // 不取的,可以直接用逗号跳过
  5. const [e, [, , f], g] = [1, [2, 4, 5], 3];
  6. console.log(e, f, g); // 1 5 3

获取数组中的元素
  1. const [a, b, c, d] = [1, 2, 3, 4];
  2. console.log(a, b, c, d);// 1,2,3,4

元素交互顺序
  1. let a = 1111;
  2. let b = 2222;
  3. [b, a] = [a, b];
  4. console.log(a, b); // 2222 1111

对象解构

  1. 模式(解构)匹配
  2. 属性名相同的完成赋值
  3. 取别名
  1. const { age: age, username: uname } = { username: 'Alex', age: 18 };
  2. console.log(age, uname);
  3. const {sex: sex,name: uname} = {sex: '女',name: '可可'};
  4. console.log(sex, uname);

对象解构赋值的注意事项
  1. 对象解构赋值的默认值。
    对象的属性值严格等于undefined时,对应的默认值才会生效。
    1. const {username = 'zhang', age = 0} = { username: 'alex'};
    2. console.log(username, age); //alex 0
  1. 将一个已经声明的变量用于解构赋值。
    如果将一个已经声明的变量用于对象的解构赋值,赋值需在圆括号中 。 ```javascript // let { x } = { x: 1 }; // console.log(x); //报错,因为重复声明

let x = 2; ({ x } = { x: 1 });
console.log(x); //1

  1. 3. 对象的解构赋值可以取到继承的属性或方法
  2. <a name="VW4cj"></a>
  3. ##### 获取对象中的属性(重点)
  4. ```javascript
  5. // 以前是 声明两个变量 来获取obj的两个属性
  6. const username = obj.username;
  7. const height = obj.height;
  8. // 现在用对象解构
  9. const { username, height } = obj;
  10. console.log(username, height);

其他类型解构赋值

  1. 字符串可以按数组或对象的形式解构赋值
  2. 字符串可以按数组或对象的形式解构赋值
  3. undefined 和 null 无法转为对象,解构赋值都会报错

函数参数的解构赋值

  1. // 函数参数的解构赋值
  2. const array = [1, 1];
  3. // const add = arr => arr[0] + arr[1];
  4. const add = ([x = 0, y = 0]) => x + y;
  5. console.log(add(array)); //2
  6. console.log(add([])); //0

解构加默认值

  1. // 解构 + 默认值
  2. const arr = [1,100];
  3. const [a,b ] = arr; a = 1 b = undefined
  4. const [a, b = 2] = arr;
  5. console.log(a, b); // a =1 b = 2
  6. // b = 2 默认值 (你没有,就使用我,你有,使用你的)
  7. const [a, b = 2] = arr;
  8. console.log(a,b);
  9. const obj = {
  10. username: 100,
  11. height: 500,
  12. };
  13. const {
  14. username,
  15. height = 200
  16. } = obj;
  17. console.log(username, height);

小结:

  1. 解构对象
    const { username,height } = {username:”悟空”,height:200}
  2. 解构数组
    在右边找不到对应的数据 就使用默认值
    1. const [a,b]=[100,200];
    2. //c 在右边找不到对应的数据 c 就使用默认值 300
    3. const [a,b,c=300]=[100,200];
  1. 解构 + 默认值
    1. //如果右边的对象中没有height 属性 那么 height变量 = 1000
    2. const { username,height } = {username:"悟空",height:200}
    3. const { username,height=1000 } = {username:"悟空",height:200}

展开运算符和剩余运算符

展开运算符和剩余运算符的区别

展开运算符

[3,1,2] —->3,1,2

剩余运算符

3,1,2 —->[3,1,2]

展开运算符

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

把数组或者类数组对象展开成一些列用逗号隔开的值

函数内获取
  1. function show(a, ...all) { // 只能放最后
  2. console.log(a);
  3. console.log(all);
  4. }
  5. show(1);// 1 []
  6. show(1, 2, 3);// 1 [2,3]

数组内展开
  1. function test(a, b, c) {
  2. console.log(a);
  3. console.log(b);
  4. console.log(c);
  5. }
  6. let arr = [1, 2, 3];
  7. test(...arr); //1 2 3
  1. // 将一个数组插入到另一个数据中
  2. let arr1 = [1, 2, 3];
  3. let arr2 = [...arr1, 4, 5, 6];
  4. console.log(arr2); // [1, 2, 3, 4, 5, 6]
  5. // 在数组的后面 新增一个 元素 'd'
  6. const newArr = [...arr,'d'];
  7. //在数组前面 新增一个属性 A
  8. console.log(newArr);
  9. const newArr = ['A', ...arr];
  10. console.log(newArr);
  11. arr.push
  12. arr.unshift
  13. // splice 来实现数组中间插入元素
  1. //获取数组中的最大值
  2. function getMax(...args) {
  3. let max = args[0];
  4. args.forEach((value) => {
  5. if (value > max) {
  6. max = value
  7. }
  8. });
  9. console.log(max);
  10. }
  11. getMax(12, 34, 100, 98, 56, 55)

对象内获取
  1. const obj = {
  2. name:"悟空",
  3. skill:"72变",
  4. say() {}
  5. }
  6. const {name,...others} = obj;
  7. console.log(name); // 悟空
  8. console.log(others); // {skill: "72变", say: ƒ}

对象内展开
  1. // 对象是引用类型 写 = 相当于将obj的地址 给了一份 newObj 两个变量指向的地址同样的 同一个对象
  2. const newObj = obj;
  3. newObj.color = 'yellow';
  4. console.log("新的对象",newObj);
  5. console.log("旧的对象",obj);
  6. // 建议这么做,可以互相影响
  7. const newObj = {...obj,
  8. color: 'yellow'
  9. }; // 给newObj 开辟新的内存空间
  10. //const newObj = { username:"悟空",height:20}; // 给newObj 开辟新的内存空间
  11. newObj.username = '八戒';
  12. newObj.weight = 100;
  13. console.log(obj);
  14. console.log(newObj);

剩余运算符

把逗号隔开的 值序列组合成数组

数组内剩余

  1. // 获取a后面的剩余所有参数
  2. const [a, ...rest] = [1, 2, 3, 4, 5];
  3. console.log(a); // 1
  4. console.log(rest); // [2, 3, 4, 5]
  5. // 计算数组最大值
  6. const arr = [1, 2, 3, 4];
  7. console.log(Math.max(...arr)); //4

剩余参数和 arguments对象的区别

  1. 剩余参数只包含那些没有对应形参的实参,而 arguments 对象包含了传给函数的所有实参。
  2. arguments对象不是一个真正的数组,而剩余参数是真正的 Array实例,也就是说你能够在它上面直接使用所有的数组方法,比如 sortmapforEachpop
  3. arguments对象还有一些附加的属性 (如callee属性)。

Set对象

  1. 永远不会有重复元素的对象
  2. 可以理解为不重复的数组
  1. const set = new Set([1, 5, 3, 4]);
  2. set.add(5);
  3. set.add(5);
  4. console.log(set);

Set对象转为数组

  1. const set = new Set([1, 5, 3, 4]);
  2. set.add(5);
  3. set.add(5);
  4. console.log(set); //
  5. const arr = [...set];// 将set对象转数组
  6. console.log(arr);
  7. const beforeArr = ['ha', 1, 3, 5];
  8. const newArr = new Set(beforeArr); //Set 要new、首字母要大写
  9. newArr.add(6)
  10. const afterArr = [...newArr]; //将set对象转数组
  11. console.log(newArr);

函数的四种调用模式

函数调用模式

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

  1. function fn(){
  2. console.log(this);// 指向window
  3. }
  4. fn();

方法调用模式

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

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

构造函数调用模式

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

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

方法借用模式

上下文调用模式(借用方法模式)

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

call

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

  1. const RichWumon = {
  2. name: "富婆",
  3. say: function () {
  4. console.log(this.name, " 我要重金求子");
  5. }
  6. }
  7. const obj = {
  8. name: "帅哥"
  9. }
  10. RichWumon.say(); // 富婆
  11. RichWumon.say.call(obj); // 帅哥

call应用
  1. let divs = document.querySelectorAll('div');
  2. // let divs = document.body.children;
  3. console.log(divs);
  4. function change(nodelist) {
  5. console.log(Object.prototype.toString.call(nodelist));
  6. return Array.prototype.slice.call(nodelist);
  7. }

apply

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

  1. const RichWumon = {
  2. name: "富婆",
  3. say: function () {
  4. console.log(this.name, " 我要重金求子");
  5. }
  6. }
  7. const obj = {
  8. name: "帅哥"
  9. }
  10. RichWumon.say(); // 富婆
  11. RichWumon.say.apply(obj); // 帅哥

apply应用

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

bind

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

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

call、apply、bind传参数写法

传递参数

  1. obj.skill.call(person,参数1,参数2)
  2. obj.skill.apply(person,[参数1,参数2])
  3. const func = obj.skill.bind(person);
    func(参数1,参数2)
  1. // 传递参数的时候不同写法
  2. const obj = {
  3. name: '老王',
  4. skill(a, b) {
  5. console.log(this.name + ' ' + a + ' ' + b);
  6. },
  7. };
  8. const person = {
  9. name: '大郎',
  10. };
  11. obj.skill.call(person, 1, 2); // 传参 // 大郎 1 2
  12. obj.skill.apply(person, [1, 2]); // 数组形式来传参 // 大郎 1 2
  13. const func = obj.skill.bind(person);
  14. func(1, 2); // 大郎 1 2
  15. // 借用 Math.max方法,但目的不是修改this指向, 而是计算数组最大值
  16. const arr = [1, 2, 3, 4];
  17. const max = Math.max.apply(null, arr); //接受数组
  18. console.log(max); //4
  19. // bind call apply 都可以实现修改this指向

call、apply、bind写法区别

  1. const obj = {
  2. name: '老王',
  3. skill() {
  4. console.log(this.name + '哈哈');
  5. }
  6. }
  7. const person = {
  8. name: '老例',
  9. }
  10. // call方法改变this指向
  11. obj.skill.call(person);
  12. // apply方法改变this指向
  13. obj.skill.apply(person);
  14. // bind方法改变this指向
  15. // 不会直接调用函数,而是返回一个新的函数
  16. let func = obj.skill.bind(person);
  17. // 然后主动调用新的函数-调用skill
  18. func();