JavaScript高级复习上(59th)

1、类 constructor 构造函数

constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示定义,类内部会自动给我们创建一个 constructor()

语法:

// 创建一个学生类
class Student {
    constructor(uname, age, major) {
        this.uname = uname;
        this.age = age;
        this.major = major;
    }
}

类的实例化——创建对象

let Ty = new Student("TY", 22, "CS");
console.log(Ty.uname); // TY

注意:

1、通过 class 关键字创建类, 类名我们还是习惯性定义首字母大写
2、类里面有个 constructor 函数,可以接受传递过来的参数,同时返回实例对象
3、constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数
4、生成实例 new 不能省略
5、最后注意语法规范, 创建类:类名后面不要加小括号。生成实例:类名后面加小括号, 构造函数不需要加 function

2、类中添加方法

语法:直接在类中写方法名和括号即可,如下所示。

class Student {
    constructor(uname, age, major) {
        this.uname = uname;
        this.age = age;
        this.major = major;
    }
    // 类中添加方法
    sing() {
        console.log(this.uname + "会唱歌");
    }
}

创建实例:

let Ty = new Student("TY", 22, "CS");
Ty.sing(); // Ty会唱歌

注意:
方法之间不能加逗号分隔,同时方法不需要添加 function 关键字。

3、类的继承

1、继承

1、现实中的继承:子承父业,比如我们都继承了父亲的姓。
2、程序中的继承:子类可以继承父类的—些属性和方法。
语法:使用 extends 关键字。

class Son extends Father {
    // class body
}

2、super 关键字

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

class Person {
    constructor (uname, age) {
        this.uname =uname;
        this.age = age;
    }
}
class Student extends Person {
    constructor (uname, age, major) {
        // super 将子类的参数传递给父类构造函数,减少代码量
        super(uname, age);
        // 子类可以有自己独有的属性
        this.major = major;
    }
}
let rick = new Student("Rick", 22, "数学");

3、super 调用父类普通函数

class Parent {
    sayHi() {
        return "Father: hello";
    }
}
class Child extends Parent {
    sayHi() {
        // super 调用父类普通函数
        console.log(super.sayHi());
    }
}
let man = new Child();
man.sayHi(); // Father: hello

4、super 必须放到子类 this 之前

子类在构造函数中使用 super, 必须放到 this 前面。

class Father {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}
class Son extends Father {
    constructor(x, y, z) {
        super(x, y, z);
        this.z = this.z;
    }
}
let obj = new Son();

4、使用类的注意点

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

5、构造函数存在的问题

构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

虽然构造函数方法很好用,但是存在浪费内存的问题。
在这里插入图片描述
函数内容一样的函数sing(),会在内存中产生两份,占两份空间。 我们希望所有的对象使用同一个函数,这样就比较节省内存,于是就有了构造函数原型对象 prototype

6、构造函数原型对象prototype

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

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

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

关键概念理解:

1、原型是一个对象,我们也称为 prototype 为原型对象。
2、原型的作用:共享方法

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);
console.log(ldh.sing === zxy.sing); // true

上述代码将输出 true,因为 sing 方法已被共享。

7、对象原型 __ proto __

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

2、 __ proto __ 对象原型和构造函数的原型对象 prototype 是等价的,都是对象。

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

4、__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
在这里插入图片描述

8、constructor 构造函数

1、对象原型__proto__和构造函数原型对象prototype里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。 constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。

2、一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以 给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。

function Human(uname, age) {
    this.uname = uname;
    this.age = age;
}
Human.prototype = {
    // 添加一个 constructor 指向原来的构造函数
    constructor: Human,      //
    sayHi: function () {
        console.log("Hey!");
    },
    eat: function () {
        console.log("Eat something.");
    }
}
let rick = new Human('rick', 35);

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

在这里插入图片描述

10、原型链(重点)

在这里插入图片描述
1.只要是对象就有__proto__ 原型, 指向原型对象
2.我们Star原型对象里面的__proto__原型指向的是 Object.prototype
3.我们Object.prototype原型对象里面的__proto__原型 指向为 null

11、JavaScript 的成员查找机制(规则)

1.当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
2.如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
3.如果还没有就查找原型对象的原型(Object的原型对象)。
4.依此类推一直找到 Object 为止(null)。
5.__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。

12、自己总结的原型链

说起原型链就要先说一下构造函数,什么是构造函数呢,它可以用来创建对象实例,当我们new了构造函数就会创建一个实例,但是构造函数有一个问题就是里面存放的函数方法每new一次就会多开辟一份内存空间,为了解决这个问题构造函数里面有一个属性叫prototype原型对象,专门存放公有的方法,实例对象怎么调用这个方法呢,因为所有对象身上都有对象原型__proto__,就可以通过它来调用,同时对象身上都有一个constructor 属性可以指回构造函数本身,原型链其实就是一个查找的过程,当我们想要查找对象实例身上的属性时先在本身找,找不到再用对象原型__proto__去原型对象prototype上找,再找不到再去原型对象的prototype上找,最终找不到返回最终指向null

13、扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能
注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {}
只能是 Array.prototype.xxx= function(){} 的方式。

14、改变函数内部 this 指向

JavaScript 为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部 this 的指向问题,常用的有 bind()call()apply() 三种方法。

1、call方法

call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

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

参数说明:

1、thisArg:在 fun 函数运行时指定的 this 值
2、arg1,arg2:传递的其他参数
3、返回值就是函数的返回值,因为它就是调用函数
4、因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承36

2、apply方法(数组)

apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。

fun.apply(thisArg, [argsArray])

1、thisArg:在fun函数运行时指定的 this 值
2、argsArray:传递的值,必须包含在数组里面
3、返回值就是函数的返回值,因为它就是调用函数
4、因此 apply 主要跟数组有关系,比如使用 Math.max() 求数组的最大值

let arr = [-10, 2, 12, 3, 1];
let max = Math.max.apply(Math, arr);
console.log(max); // 12

案例:

function fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}

fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window

当第一个参数为null、undefined的时候,默认指向window(在浏览器中)

3、bind方法(不会调用)

bind() 方法不会调用函数。但是能改变函数内部 this 指向。

let fn = fun.bind(thisArg, arg1, arg2, ...)

1、thisArg:在 fun 函数运行时指定的 this 值
2、arg1,arg2:传递的其他参数
3、返回由指定的 this 值和初始化参数改造的 原函数拷贝
4、因此当我们只是想改变 this 指向,并且不想调用这个函数的时候,可以使用 bind
5、bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)
6、改变this指向后不会立即执行,而是返回一个永久改变this指向的函数

unction fn(...args){
    console.log(this,args);
}
let obj = {
    myname:"张三"
}

const bindFn = fn.bind(obj); // this 也会变成传入的obj ,bind不是立即执行需要执行一次
bindFn(1,2) // this指向obj
fn(1,2) // this指向window

4、apply、call、bind三者的区别

1、三者都可以改变函数的this对象指向
2、三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window
3、三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入
4、bind是返回绑定this之后的函数,不会调用函数,apply、call 则是立即执行

15、数组新增方法

迭代(遍历)方法:forEach()、map()、filter()、some()、every()

1、forEach()

forEach 方法用于遍历数组,不对原数组进行修改,没有返回值。

array.forEach(function(Value, index, arr), thisArg);

该方法接收一个函数 function 参数,其中,该函数内又有三个参数:

1、Value:数组当前项的值,最后输出Value就可以实现遍历了
2、index:数组当前项的索引
3、arr:数组对象本身
一般可以省略后面回调函数中后两个参数和 thisArg 参数,即:

array.forEach(function(Value));
<body>
    <script>
        // forEach 迭代(遍历) 数组
        var arr = [1, 2, 3];
        var sum = 0;
        arr.forEach(function(value, index, array) {
            console.log('每个数组元素' + value);
            console.log('每个数组元素的索引号' + index);
            console.log('数组本身' + array);
            sum += value;
        })
        console.log(sum);
    </script>
</body>

2、map()

map() 方法遍历一个数组,首先创建一个新数组,新数组中的每个元素是是调用一次所提供的函数参数后的返回值,然后返回这个新数组。

let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let mapResult = numbers.map((item, index, array) => item * 2);
console.log(mapResult) // 2,4,6,8,10,8,6,4,2

3、filter()

对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回

let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let filterResult = numbers.filter((item, index, array) => item > 2);
console.log(filterResult); // 3,4,5,4,3

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组,注意它返回一个新数组。

4、some()

对数组每一项都运行传入的测试函数,如果至少有1个元素返回 true ,则这个方法返回 true

let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let someResult = numbers.some((item, index, array) => item > 2);
console.log(someResult) // true

通俗点:查找数组中是否有满足条件的元素

5、every()

对数组每一项都运行传入的测试函数,如果所有元素都返回 true ,则这个方法返回 true

let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let everyResult = numbers.every((item, index, array) => item > 2);
console.log(everyResult) // false

16、Object.defineProperty()

Object.defineProperty() 定义对象中新属性或修改原有的属性。

Object.defineProperty(obj, prop, descriptor)

参数:

1、obj:必需。目标对象
2、prop:必需。需定义或修改的属性的名字
3、descriptor:必需。目标属性所拥有的特性
Object.defineProperty() 第三个参数 descriptor 说明: 以对象形式 { } 书写。

// descriptor 对象的参数值均为默认值
Object.defineProperty(obj, prop, {
    value: undefined,
    writable: false,
    enumerable: false,
    configurable: false
})

1、value:设置属性的值 默认为 undefined
2、writable:值是否可以重写。true | false 默认为 false
3、enumerable:目标属性是否可以被枚举。true | false 默认为 false
4、configurable:目标属性是否可以被删除或是否可以再次修改特性 true | false,默认为 false

descriptor 对象的参数默认值说明,使用 defineProperty 新增一个对象属性,该属性只读,不可迭代,不可再被修改特性。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值