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
语法:直接在类中写方法名和括号即可,如下所示。
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 关键字。
1、现实中的继承:子承父业,比如我们都继承了父亲的姓。
2、程序中的继承:子类可以继承父类的—些属性和方法。
语法:使用 extends
关键字。
class Son extends Father {
// class body
}
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, "数学");
class Parent {
sayHi() {
return "Father: hello";
}
}
class Child extends Parent {
sayHi() {
// super 调用父类普通函数
console.log(super.sayHi());
}
}
let man = new Child();
man.sayHi(); // Father: hello
子类在构造函数中使用 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();
1、在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
2、类里面的共有属性和方法一定要加 this 使用
3、类里面的 this 指向问题:constructor 里面的 this 指向实例对象, 方法里面的 this 指向这个方法的调用者
构造函数是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与 new 一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。
虽然构造函数方法很好用,但是存在浪费内存的问题。
函数内容一样的函数sing()
,会在内存中产生两份,占两份空间。 我们希望所有的对象使用同一个函数,这样就比较节省内存,于是就有了构造函数原型对象 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 方法已被共享。
1、任何对象身上都会有一个属性 __ proto __,指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __ proto __原型的存在。
2、 __ proto __ 对象原型和构造函数的原型对象 prototype 是等价的,都是对象。
3、方法的查找规则: 首先先看ldh 对象身上是否有 sing 方法,如果有就执行这个对象上的sing, 如果没有sing
这个方法,因为有__proto__ 的存在,就去构造函数原型对象prototype身上去查找sing这个方法
4、__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype
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);
1.只要是对象就有__proto__ 原型, 指向原型对象
2.我们Star原型对象里面的__proto__原型指向的是 Object.prototype
3.我们Object.prototype原型对象里面的__proto__原型 指向为 null
1.当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
2.如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
3.如果还没有就查找原型对象的原型(Object的原型对象)。
4.依此类推一直找到 Object 为止(null)。
5.__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
说起原型链就要先说一下构造函数,什么是构造函数呢,它可以用来创建对象实例,当我们new了构造函数就会创建一个实例,但是构造函数有一个问题就是里面存放的函数方法每new一次就会多开辟一份内存空间,为了解决这个问题构造函数里面有一个属性叫prototype原型对象,专门存放公有的方法,实例对象怎么调用这个方法呢,因为所有对象身上都有对象原型__proto__,就可以通过它来调用,同时对象身上都有一个constructor 属性可以指回构造函数本身,原型链其实就是一个查找的过程,当我们想要查找对象实例身上的属性时先在本身找,找不到再用对象原型__proto__去原型对象prototype上找,再找不到再去原型对象的prototype上找,最终找不到返回最终指向null
可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义求偶数和的功能
注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {}
,
只能是 Array.prototype.xxx= function(){}
的方式。
JavaScript 为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部 this 的指向问题,常用的有 bind()
、call()
、apply()
三种方法。
call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。
fun.call(thisArg, arg1, arg2, ...);
参数说明:
1、thisArg:在 fun 函数运行时指定的 this 值
2、arg1,arg2:传递的其他参数
3、返回值就是函数的返回值,因为它就是调用函数
4、因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承36
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(在浏览器中)
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
1、三者都可以改变函数的this对象指向
2、三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window
3、三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入
4、bind是返回绑定this之后的函数,不会调用函数,apply、call 则是立即执行
迭代(遍历)方法:forEach()、map()、filter()、some()、every()
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>
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
对数组每一项都运行传入的函数,函数返回 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()
方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组,注意它返回一个新数组。
对数组每一项都运行传入的测试函数,如果至少有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
通俗点:查找数组中是否有满足条件的元素
对数组每一项都运行传入的测试函数,如果所有元素都返回 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
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 新增一个对象属性,该属性只读,不可迭代,不可再被修改特性。