JavaScript 知识梳理

1.变量

1.1 变量规则

  • 变量只能包含字母、下划线、美元符号或数字;
  • 第一个字符不能是数字。
  • ECMAScript 的变量是松散类型的 —— 可以用来保存任何类型的数据,即每个变量仅仅是一个用于保存值的占位符而已。

1.2 定义变量

1.2.1 var —— ES5及之前使用

var name = "jane";

var 没有块级作用域,只有函数级作用域和全局作用域:

  • 函数级作用域:
    在函数内用 var 定义一个变量,则该变量为定义该变量的作用域中的局部变量,函数退出后则销毁。
  • 全局作用域:
    省略 var ,则为全局变量,在函数外的任何地方都可被访问到(不推荐,难维护,且在严格模式下会抛出错误)。

1.2.2 let & const —— ES6 新增

ES6 中增加了 let 和 const :

let 用来定义变量,定义后可更改;
const 用来定义常量,定义后不可更改。

let 和 var 的区别:

  • let 是块级作用域,var 是函数级作用域,let 的作用域更小;
  • let 无变量提升。
    下面定义的变量,在上面使用会报错;
    var有变量提升,下面定义的变量,在上面值为undefined,不会报错。
  • let 同一个变量只能声明一次,而 var 可声明多次。

2. 数据类型

JavaScript 中有基本类型和引用类型两类共七种数据类型。

undefined 用于未定义的值 —— 只有一个 undefined 值的独立类型。
null 用于未知的值 —— 只有一个 null 值的独立类型。
boolean 用于 true 和 false。
number 数字:整数或浮点数。
string 字符串:一个字符串可以包含一个或多个字符。
symbol 用于唯一的标识符。
object 更复杂的数据结构,用于储存数据集合和更复杂的实体。

2.1 基本类型

2.1.1 Undefined

定义:

let a = undefined;

使用var声明变量但未初始化,或初始化其值为undefined。

(var message);alert(message);//undefined 

2.1.2 Null

定义:

let a = null;

空对象指针,用于在将来保存对象。这样只要检查变量是否等于null,就知道这个变量内是否已经保存了一个对象的引用。

注:

alert(null == undefined); //true
alert(null === undefined); //false

2.1.3 Boolean

定义:

let a = true;
let b = false;

ECMAScript 中所有类型的值都有与这两个 Boolean 值等价的值,可使用转型函数 Boolean() 进行转换。

  • 非空字符串、非零数值、任何对象都是 true;
Boolean('111') === true
  • 空字符串、数值零或NaN、null、undefined 都是false。
Boolean('text') === true//true
Boolean(2) === true//true
Boolean({}) === true//true
Boolean({name: 'jane'}) === true//true
  • 流控制语句(如if)会自动执行 Boolean 转换。
var message = 'hello';
if(message){alert('value is true');}  //message被自动转换为对应的 Boolean 值(true)。

2.1.4 Number

2.1.4.1 定义

//整数
let num = 12;
//八进制:第一位为0,后面数值若超出范围,则前导零被忽略,将后面数值按十进制解析
let num1 = 070;//八进制的56
let num2 = 079;//无效的八进制数值,解析为79
//十六进制:前两位为0x,后跟十六进制数字
let num3 = 0xa;  //十六进制的10
let num4 = 0x1f;  //十六进制的31

2.1.4.2 NaN

NaN(非数值)——特殊的数值,本该返回数值的操作数未返回数值的情况,不抛出错误。

1.任何涉及 NaN 的操作都会返回 NaN ,NaN 与任何值都不相等。

alert(NaN == NaN);   //false

2.isNaN()

isNaN() 函数收到一个值后,会尝试将这个值转换为数值,可以转换则返回 false,否则返回 true。

isNaN(true)//false
isNaN(false)//false
isNaN(1)//false
isNaN('1')//false
isNaN('2')//false
isNaN(null)//false
isNaN(undefined)//true
isNaN({})//true
isNaN(function(){})//true
isNaN('2/0')//true

2.1.4.3 数值转换

Number():将任何类型转换为数值;

parseInt():将字符串转换为整数;

parseFloat():将字符串转换为浮点数;

Number()
var num1 = Number("Hello world!"); //NaN
var num2 = Number(""); //0
var num3 = Number("000011"); //11
var num4 = Number(true); //1
parseInt()
var num1 = parseInt("1234blue"); // 1234
var num2 = parseInt(""); // NaN
var num4 = parseInt(22.5); // 22
var num6 = parseInt("70"); // 70
var num8 = parseInt("s2131"); // NaN
var num = parseInt("0xAF", 16); //175
var num1 = parseInt("AF", 16); //175
var num2 = parseInt("AF"); //NaN
指定基数转换:
var num1 = parseInt("10", 2); //2 (按二进制解析)
var num2 = parseInt("10", 8); //8 (按八进制解析)
var num3 = parseInt("10", 10); //10 (按十进制解析)
var num4 = parseInt("10", 16); //16 (按十六进制解析)
parseFloat()
从第一个字符开始解析,始终忽略前导零
var num1 = parseFloat("1234blue"); //1234 (整数)
var num2 = parseFloat("0xA"); //0
var num3 = parseFloat("22.5"); //22.5
var num4 = parseFloat("22.34.5"); //22.34
var num5 = parseFloat("0908.5"); //908.5
var num6 = parseFloat("3.125e7"); //31250000

2.1.5 String

  • String 类型用于表示由零或多个16 位Unicode 字符组成的字符序列,即字符串。
  • 字符串可以由双引号(")或单引号(')表示,但前后符号应保持一致。

2.1.5.1 定义

var firstName = "Nicholas";
var lastName = 'Zakas';

2.1.5.2 toString()

  1. 数值、布尔值、对象和字符串都有一个 toString() 方法,返回一个对应字符串。
let num = 2; num.toString();//"2"
let flag = true; flag.toString();//"true"
let text = 'hello'; text.toString();//"hello"

数值的 toString() 方法可以传一个参数:输出数值的基数,如下:

var num = 10;
alert(num.toString()); // "10"  不传,默认为10
alert(num.toString(2)); // "1010"
alert(num.toString(8)); // "12"
alert(num.toString(10)); // "10"
alert(num.toString(16)); // "a"
  1. 对象的 toString() 方法
let obj = {name: 'jane'};
obj.toString() //"[object Object]"`
  1. null 和 undefined 没有 toString() 方法。

  2. String():
    不知道要转换的值是不是 null 或 undefined 时,可以使用转型函数 String(),这个函数可以将任何类型的值转换为字符串。String()遵循下列转换规则:

  • 如果值有toString()方法,则调用该方法(没有参数)并返回相应的结果;
  • 如果值是null,则返回"null";
  • 如果值是undefined,则返回"undefined"。

2.1.5.3 String 的其他常用方法

let testStr = 'testStr';
testStr.substr(1,3) //"est" 截取 testStr 从第1个开始,取3个
testStr.substring(1,4)  //"est" 截取 testStr 从第1个开始,到第4个,不包含第4个
testStr.slice(1,4)  //"est" 截取 testStr 从第1个开始,到第4个,不包含第4个
testStr.substring(2,0)  //"te"
testStr.slice(2,0)  //""

2.2 引用类型 —— 对象

JavaScript 中,对象是及其重要的一点。其他六种是原始类型,他们的值只包含一种东西,而对象是用来存储键值对和更复杂的实体的。

2.2.1 创建对象

//“构造函数”语法创建(后面会详细介绍构造函数)
let info = new Object()
//“字面量”语法创建 —— 常用
let info = {
    name: 'jane',   //键"name",值"jane"
    age: 17 //键"age",值17
}

2.2.2 对象操作

先创建一个对象

let info = {
    name: 'jane',   //键"name",值"jane"
    age: 17 //键"age",值17
}

2.2.2.1 添加属性

//使用 .
info.address = 'beijing'//"beijing",此时的 info:{name: "jane", age: 17, address: "beijing"}

//使用 []
info['friend'] = 'tom'//"tom",此时的 info:{name: "jane", age: 17, address: "beijing", friend: "tom"}

2.2.2.2 计算属性

计算属性 —— 在对象字面量中使用方括号,方括号内可以是任何属性名或变量

let s = 'sch'.concat('ool);
info[s] = 'HIT';//此时的 info:{name: "jane", age: 17, school: "HIT"}

2.2.2.3 删除属性

delete info.age//true,此时的 info:{name: "jane", address: "beijing", friend: "tom"}

2.2.2.4 获取对象属性值

//使用 .
info.address//"beijing"

//使用 []
info['friend']//"tom"

2.2.2.5 对象的可枚举与不可枚举

对象的每个属性都有一个描述对象(Descriptor),用来控制该属性。

Object.getOwnPropertyDescriptor() 方法返回指定对象上一个自有属性对应的属性描述符。

(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)

语法:

Object.getOwnPropertyDescriptor(obj, prop)
//obj:需要查找的目标对象, prop:目标对象内属性名称

let info = {
    name: 'jane',   //键"name",值"jane"
    age: 17 //键"age",值17
}
let des = Object.getOwnPropertyDescriptor(info, 'name')
//des
{
    value: "jane", //该属性的值
    writable: true,   //当且仅当属性的值可以被改变时为true
    enumerable: true,   //当且仅当指定对象的属性可以被枚举出时,为 true
    configurable: true  //当且仅当指定对象的属性描述可以被改变或者属性可被删除时,为true
}

不可枚举(enumerable:false): 有以下操作会忽略不可枚举的属性。

  • for...in:不可枚举的属性不会被遍历。
  • Object.keys():不可枚举的属性不会被遍历。
  • JSON.stringify():不可枚举的属性不会被转换为字符串。
  • Object.assign(): 不可枚举的属性不会被复制。

2.2.3 对象常用方法

先创建一个对象

let info = {
    name: 'jane',   //键"name",值"jane"
    age: 17 //键"age",值17
}

2.2.3.1 属性检查

1. hasOwnProperty —— object.hasOwnProperty(propertyName),propertyName 必须为字符串

info.hasOwnProperty('name') //true
info.hasOwnProperty('language') //false

2. "key" in object —— key 为要检测的属性

"name" in info  //true
"language" in info  //false

2.2.3.2 对象遍历

先创建一个对象

let info = {
    name: 'jane',   //键"name",值"jane"
    age: 17 //键"age",值17
}
Object.defineProperty(info, 'name', {
  enumerable: true //可枚举
});
Object.defineProperty(info, 'age', {
  enumerable: false //可枚举
});
//在原型链上添加属性 —— 先了解,原型链部分后再看
Object.defineProperty(info, 'eg1', {
  value: 'view',
  enumerable: true //可枚举
});
Object.defineProperty(info, 'eg2222', {
  value: 'unview',
  enumerable: false //不可枚举
})

1. for ... in —— 先遍历对象自身的可枚举属性,再遍历继承的可枚举属性。

for (var index in info) {
  console.log('key:', index, 'value:', info[index])
}
//result 只遍历了自身及原型链上的可枚举属性
key: name value: jane
key: eg1 value: view

2. Object.keys() —— 返回自身及原型链上的可枚举属性组成的数组,可通过遍历数组来遍历对象。

Object.keys(info)
//result 返回自身及原型链上的可枚举属性组成的数组
["name", "eg1"]

Object.keys(info).forEach(item=>{
  console.log(item, info[item])
})
//result
name jane
eg1 view

3. Reflect.ownKeys() —— 返回自身及原型链上所有属性组成的数组,可通过遍历数组来遍历对象。

Reflect.ownKeys(info)
//result 返回自身及原型链上的所有属性组成的数组,包括不可枚举属性
["name", "age", "eg1", "eg2222"]

Reflect.ownKeys(info).forEach(item=>{
  console.log(item, info[item])
})
//result
name jane
age 17
eg1 view
eg2222 unview

4. Object.getOwnPropertyNames() —— 返回自身及原型链上所有属性组成的数组(包括不可枚举属性),可通过遍历数组来遍历对象。

Object.getOwnPropertyNames(info)
//result 返回自身及原型链上的所有属性组成的数组,包括不可枚举属性
["name", "age", "eg1", "eg2222"]

Object.getOwnPropertyNames(info).forEach(item=>{
  console.log(item, info[item])
})
//result
name jane
age 17
eg1 view
eg2222 unview

2.3 变量赋值

基本类型数据赋值:
从一个基本类型变量向另一个变量赋值时,会在内存中新建一个地址,存放新的变量和复制过来的值;

let a = 1;
let b = a;
b = 3;  
console.log(a, b)// =>  a = 1; b = 3; a 和 b 的值存在于内存中的两个地址,互不影响。

引用类型数据赋值:
从一个引用类型变量向另一个变量赋值时,同上,但引用类型的值,实际上是一个指针,与初始变量指向同一个堆内存的对象。因此,这两个变量会互相影响。

let s = {name:"jane"};let y = s; y.name="tom";console.log(s, y)
let a = {name: "jane"};
let b = a;
b.name = "tom"; 
b.city="cc";
console.log(a, b) // =>  {name: "tom", city: "cc"} {name: "tom", city: "cc"} a 和 b 的值存在于内存中的两个地址,但值是同一个指针,指向同一个内存中的对象,属性的改变会相互影响。

3. 函数 & 原型 & 继承

3.1 函数

3.1.1 函数声明:

//语法:
function 函数名(参数1, 参数2,...,参数N) {
    函数体
}
//eg:
function sayHi(name, message) {
    alert("Hello " + name + "," + message);
}

3.1.2 函数调用

//语法
functionName(arg0, arg1,...,argN);
//eg:
sayHi("Nicholas", "how are you today?");//弹出 "Hello Nicholas,how are you today?"

3.1.3 变量作用域

  • 函数内声明的变量,只在该函数内部可见。
function sayHi() {
  let name = 'jane';
  let age = 18;
  alert(name + ":" + age);
}
console.log('age:', age);//age is not defined
  • 函数外声明的变量,函数可以访问,也可以修改。
let age = 20;
function sayHi() {
  let name = 'jane';
  age = 18;
  console.log(name + ":" + age);
}
sayHi() //jane:18

3.1.4 函数返回值

任何函数、任何时候都可以通过 return 语句后跟要返回的值来是实现返回值。函数会在执行完 return 语句后停止并立即退出。因此,return 之后的任何代码都永远不会执行。

function sum(num1, num2) {
    return num1 + num2;
}
let result = sum(5, 10);  // result 为15

注:

  • return 可以不带任何返回值。则函数将返回 undefined 值。一般用在需要提前停止函数执行,又不需要返回值的情况下。
  • 推荐:要么让函数始终都有返回值,要么就永远都不要有返回值,否则会给代码调试带来不便。

严格模式下的限制: 发生以下情况,会导致语法错误,代码无法执行。

  • 不能把函数/参数命名为eval 或arguments;
  • 不能出现两个命名参数同名的情况。

3.1.5 参数

参数不是必需的,可以不传,个数及类型,即使个数与定义的个数不同,也不会报错。因为参数在内部使用一个数组来表示的。
函数体内部可以通过 arguments 对象来访问这个参数数组,从而获取传递过来的每一个参数。

function sayHi(name, message) {
    alert("Hello " + name + "," + message);
}
sayHi('jane', 'nice to meet you!')//Hello jane,nice to meet you!
//重写
function sayHi() {
    alert("Hello " + arguments[0] + "," + arguments[1]);
}
sayHi('jane', 'nice to meet you!')//Hello jane,nice to meet you!
  • arguments 对象的长度由传入函数的参数决定;
  • 没有传递值的命名参数自动被赋予 undefined 值,类似于定义了变量但未初始化。

严格模式下:

重写arguments 的值会导致语法错误(代码将不会执行)

3.1.6 ES6 函数新特性

1. 参数默认值

声明函数时直接为参数设置默认值,若调用函数时未赋值,则参数的值为默认值。

function sayHi(name, message='nice to meet you!') {
    console.log("Hello " + name + "," + message);
}
sayHi('jane', 'yeah!')  //Hello jane,yeah!
sayHi('tom')  //Hello tom,nice to meet you!

若默认值为表达式,则执行时才计算其值

let num = 10;
function sayHi(name, age=num+8) {
    console.log(name + ", age:" + age);
    num+=10;
}
sayHi('jane')  //jane, age:18
sayHi('tom')  //tom, age:28
sayHi('lucy', 12)   //lucy, age:12

2. rest 参数
...变量名,用于获取函数的剩余参数,将其放在数组中。
rest 参数必须是最后一个参数,否则会报错。

function sum(...nums){
  let sum = 0;
  nums.forEach(item => {
    sum += item;
  })
  return sum
}
let result1 = sum()   //0
let result2 = sum(1,2,3,4)   //10

3. 扩展运算符
...变量名,用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。

//对象
let obj1 = {name: 'jane', age: 18};
let obj2 = {color: 'yellow', ...obj1} //obj2: {color: "yellow", name: "jane", age: 18}
//等价于
let obj3 = Object.assign({color: 'yellow'}, obj1)   //obj3: {color: "yellow", name: "jane", age: 18}

//数组
let arr1 = [1,2,3]
let arr2 = [4,5,6]
let arr3 = [7,8,9]
let arr4 = [...arr1, ...arr2, ...arr3]  //[1, 2, 3, 4, 5, 6, 7, 8, 9]
//等价于
let arr5 = arr1.concat(arr2, arr3)  //[1, 2, 3, 4, 5, 6, 7, 8, 9]

4. 箭头函数

//箭头函数定义
let sum = (a,b) => {
  return a+b
}
//等同于
let sum = function(a,b) {
  return a+b
}
  • 箭头函数的 this 指向的是定义时的 this 对象,而不是执行时的 this 对象
  • 箭头函数是匿名函数 ,不能作为构造函数
  • 箭头函数不可以使用 arguments,可使用 rest 参数

5. name
ES6 的 name 会返回函数名。

function sum(...nums){
  let sum = 0;
  nums.forEach(item => {
    sum += item;
  })
  return sum
}
console.log(sum.name);    //"sum"

3.2 构造函数

1. 构造函数: 一种特殊的函数。

  • 主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new操作符一起使用在创建对象的语句中。
  • 我们可以使用构造函数来创建多个类似的对象,而免于代码重复。
  • JavaScript 内置了一些构造函数

2. 构造函数特点

  • 首字母大写(通常约定)
  • 只能用 new 操作符来执行
  • 内部使用的 this 对象,会指向即将生成的实例对象

3. 实例化一个对象的过程

new 一个新对象 => this 指向这个新对象 => 执行代码(对 this 赋值)=> 返回 this

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.displayInfo = function() {
        console.log(`name: ${name}, age: ${age}`)
    }
}
let jane = new Person('jane', 12)
jane.displayInfo()  //name: jane, age: 12
let tom = new Person('tom', 18)
tom.displayInfo()   //name: tom, age: 18

JavaScript 内置了很多构造函数,如:
Object、Function、Array、RegExp、Date、Number、String、Boolean。

Object 在 2.2 引用类型 已经详细介绍过了。
在 JavaScript 中,Function、Array、RegExp、Date、Number、String、Boolean 是构造函数,也有自己的构造函数,他们的构造函数就是 Object,即 Object 是其他构造函数的构造函数。

后面还会介绍一个 Math 对象,Math 不是对象的类,没有构造函数,但作为 JavaScript 内置的对象,可直接调用来进行数学方法的处理。

3.2.1 Function

3.2.1.1 函数声明:

//1. 构造函数创建 new Function
let 函数名 = new Function(参数1, 参数2,...,参数N, 函数体)
//eg:
let sayHi = new Function("name", "message", "alert('Hello " + name + "," + message)')

sayHi3('jane','hi')//Hello jane,hi

但实际的代码中不使用这种方法来创建函数,还是会用字面量的方法去创建,关于函数的详细介绍,见 3.1 函数

3.2.2 Array

3.2.2.1 创建数组

创建数组:

//使用new Array()
let arr1 = new Array(); //[]
let arr2 = new Array(5); //[empty × 5]
let arr3 = new Array('beijing','tianjin',12);//["beijing", "tianjin", 12]

//使用方括号 —— 常用
let arrs1 = [];//[]
let arrs2 = ['beijing','tianjin',12];//["beijing", "tianjin", 12]

数组内的元素可以是任何类型:

let arr = ['beijing', {name: 'jane'}, 12, true, function(){console.log('fn')}];
//0: "beijing"
//1: {name: "jane"}
//2: 12
//3: true
//4: ƒ ()

数组编号从 0 开始:

let arr = ['beijing','tianjin',12];
//arr[0]: 'beijing'
//arr[1]: 'tianjin'
//arr[2]: 12

通过 数组名[下标] 来获取或设置值:

let arr = ['beijing','tianjin',12];
console.log(arr[0]);// 'beijing'
arr[3] = true;
console.log(arr); //["beijing", "tianjin", 12, true]

3.2.2.2 常用方法

判断是否为数组:Array.isArray

//Array.isArray —— 是则返回 true,否责返回 false
let a = [1,2,3];
let b = 'str';
let b = [];
console.log(Array.isArray(a), Array.isArray(b), Array.isArray(c));  //  true false true

//unshift —— 在数组最前面添加元素,可添加多个
arr.unshift('a', 'b', 'c')
console.log(arr);//arr: ["a", "b", "c", 1, 2, 3, 4, 5, 6]

添加数组元素:push、unshift

//push —— 在数组最后添加元素,可添加多个
let arr = [1,2,3];
arr.push(4,5,6);
console.log(arr);//arr: [1,2,3,4,5,6]

//unshift —— 在数组最前面添加元素,可添加多个
arr.unshift('a', 'b', 'c')
console.log(arr);//arr: ["a", "b", "c", 1, 2, 3, 4, 5, 6]

删除数组元素:pop、shift

//pop() —— 删除并返回数组的最后一个元素
console.log(arr.pop())//6
console.log(arr);//["a", "b", "c", 1, 2, 3, 4, 5]

//shift() —— 删除并返回数组的第一个元素
console.log(arr.shift()) //a
console.log(arr); //["b", "c", 1, 2, 3, 4, 5]

替换数组中的元素:splice

//splice(start, num, newitem) —— 把 arr[start] 开始的 num 个,替换成后面的元素,不改变原数组
console.log(arr)    //[5, 4, 3, 2, 1, "c", "b"]
console.log(arr.splice(2,3,'jane')) //[3, 2, 1]
console.log(arr)    //[5, 4, "jane", "c", "b"]
console.log(arr.splice(1,1,'aa','bb', 'cc')) //[4]
console.log(arr)    //[5, "aa", "bb", "cc", "jane", "c", "b"]

替换数组中某些元素:fill

//fill(value, start, end)
let arr = [1, 2, 3, 4, 5, 5, 6, "b", "c"]
let result1 = arr.fill('hello'); 
// result1: ["hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello", "hello"]
let result2 = arr.fill('hello', 2, 5); 
//result2: [1, 2, "hello", "hello", "hello", 5, 6, "b", "c"]
let result3 = arr.fill('hello', 2, );
//result3: [1, 2, "hello", "hello", "hello", "hello", "hello", "hello", "hello"]

取数组中的元素:slice

//slice(start, end) —— 取 [arr[start], ...,  arr[end-1]],不改变原数组
console.log(arr)    //[5, 4, 3, 2, 1, "c", "b"]
console.log(arr.slice(2,5)) //[3, 2, 1]
console.log(arr)    //[5, 4, 3, 2, 1, "c", "b"]

颠倒数组中元素的顺序:reverse

//reverse —— 颠倒数组中元素的顺序
console.log(arr)    //["b", "c", 1, 2, 3, 4, 5]
console.log(arr.reverse())   //[5, 4, 3, 2, 1, "c", "b"]
console.log(arr)    //[5, 4, 3, 2, 1, "c", "b"]

数组排序:sort

//sort —— 将数组中的元素进行排序
let arrN = [1,2,4,6,12,11]
arrN.sort() //[1, 11, 12, 2, 4, 6]
arrN.sort((a, b)=>{return a-b}) //[1, 2, 4, 6, 11, 12] 从小到大排序
arrN.sort((a, b)=>{return b-a}) //[12, 11, 6, 4, 2, 1] 从大到小排序

数组转换为字符串:toString、join

//1. toString —— 将数组转换为字符串
arr.toString()  //"5,4,3,2,1,c,b

//2. join —— 通过指定的分隔符, 把数组的所有元素连接成一个字符串
arr     //[5, 4, 3, 2, 1, "c", "b"]
arr.join()  //"5,4,3,2,1,c,b"
arr.join('-')   //"5-4-3-2-1-c-b"

连接数组:concat

//concat 连接数组,并返回结果,不改变原数组
let arr1 = ['jane', 'tom']
let arr2 = ['green', 'red']
let arr3 = [true, false]
let arr4 = [1, 2, 3]
arr1.concat(arr2)  //["jane", "tom", "green", "red"]
arr1.concat(arr2, arr3, arr4) //["jane", "tom", "green", "red", true, false, 1, 2, 3]

查询数组: indexOf、lastIndexOf、find、findIndex、filter

let arr1 = [5, 4, 3, 2, 1, 5, 6, "c", "b"];
//indexOf(item) —— 从前面开始查询,返回查到的第一个 item 的下标值
arr1.indexOf(5)   //0

//lastIndexOf(item) —— 从后面开始查询,返回查到的第一个 item 的下标值
arr1.lastIndexOf(5)   //5

//includes(item) —— 判断数组中是否存在 item,存在则返回 true,不存在则返回 false
arr1.includes(0)     //false
arr1.includes(1)     //true

//find —— 遍历数组,返回使函数返回 true 的第一个元素
let arr2 = [
    { name: 'jane', age:12 },
    { name: 'tom', age:13 },
    { name: 'lucy', age:14 },
    { name: 'jimmy', age:12 }
]
let result1 = arr2.find((item, index, array) => {
    return item.age === 12
})
console.log('result1:', result1)    //result: {name: "jane", age: 12}

//findIndex —— 遍历数组,返回使函数返回 true 的第一个元素的下标
let result2 = arr2.findIndex((item, index, array) => {
    return item.age === 12
})
console.log('result2:', result2)    //result: 0

//filter —— 遍历数组,返回使函数返回 true 的所有元素组成的数组
let result3 = arr2.filter((item, index, array) => {
    return item.age === 12
})
console.log('result3:', result3)    
//result3: [{ name: 'jane', age:12 },{ name: 'jimmy', age:12 }]

检查元素:some、every

//some —— 数组中至少有一个元素在执行函数时返回的结果为 true
let arr1 = [1, 2, 3, 4, 5, 5, 6, 7]
let arr2 = [1, 2, 3, 4, 5, 5, 6, 7, 8]
let flag1 = arr1.some((item)=>{
    return item > 7
})  //flag: false
let flag2 = arr2.some((item)=>{
    return item > 7
})  //flag: true

//every —— 数组中每一个元素在执行函数时返回的结果为 true
let arr1 = [1, 2, 3, 4, 5, 5, 6, 7]
let arr2 = [1, 2, 3, 4, 5, 5, 6, 7, 8]
let flag1 = arr1.every((item)=>{
    return item > 7
})  //flag: false
let flag2 = arr2.every((item)=>{
    return item > 7
})  //flag: false
let flag3 = arr2.every((item)=>{
    return item > 0
})  //flag: true

转换数组:map

let arr = [1, 2, 3, 4, 5, 5, 6, "b", "c"]
let result = arr.map(function(item, index, array) {
  return `${item}--hello`;
})
//result:   ["1--hello", "2--hello", "3--hello", "4--hello", "5--hello", "5--hello", "6--hello", "b--hello", "c--hello"]

遍历数组:forEach

let arr = [1, 2, 3, 4]
arr.forEach(function(item, index, array) {
  console.log(item, index, array)
})
// 1 0 (4) [1, 2, 3, 4]
// 2 1 (4) [1, 2, 3, 4]
// 3 2 (4) [1, 2, 3, 4]
// 4 3 (4) [1, 2, 3, 4]

以上会改变数组的方法:
push、unshift、pop、shift、splice、reverse、sort、fill

3.2.3 Date

Date 为 JavaScript 内置的一个对象,用来存储日期、时间,并提供了一些管理它们的方法。

//创建 Date 对象
let date = new Date()//date: Wed Jun 05 2019 17:10:27 GMT+0800 (中国标准时间)

//getDay:返回 0-6,对应星期日到星期六
date.getDay()   //3

//getDate:返回 1-31,对应一个月中的 1-31 号
date.getDate()  //5

//getMonth:返回 0-11,对应1-12月
date.getMonth() //5——6月 

//getFullYear:以四位数字返回年份
date.getFullYear()  //2019

//getHours:返回小时
date.getHours()   //17
//getMinutes:返回分钟
date.getMinutes()   //10
//getSeconds:返回秒
date.getSeconds()   //27
//getMilliseconds:返回毫秒
date.getMilliseconds()  //511

//getTime:返回 1970 年 1 月 1 日至今的毫秒数
date.getTime()  //1559725827511

Date.now()

有一个特殊的方法 Date.now(),它会返回当前的时间戳,不需要整个 Date 对象。

它相当于 new Date().getTime(),但不会在中间创建一个 Date 对象。因此更快,且不会对垃圾处理造成额外的压力。

Date.now()  //1559725827511

3.2.4 RegExp

RegExp 是 JavaScript 内置的正则表达式对象,用来对字符串进行匹配。

3.2.4.1 定义正则表达式

1)字面量语法:/pattern/attributes

2)创建 RegExp 对象的语法:new RegExp(pattern, attributes);

3.2.4.2 参数

  1. pattern 是一个字符串,指定了正则表达式的模式或其他正则表达式。
  2. attributes 是一个可选的字符串,包含属性 "g"、"i" 和 "m",分别用于指定全局匹配、区分大小写的匹配和多行匹配。如果 pattern 是正则表达式,而不是字符串,则必须省略该参数。

3.2.4.3 修饰符

i  执行对大小写不敏感的匹配。
g  执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
m  执行多行匹配。

3.2.4.4 方括号——查找某个范围内的字符

[abc]  查找方括号之间的任何字符。(字母、数字、符号、中文均可)
[^abc]  查找任何不在方括号之间的字符。(字母、数字、符号、中文均可)
[0-9]  查找任何从 0 至 9 的数字。
[a-z]  查找任何从小写 a 到小写 z 的字符。
[A-Z]  查找任何从大写 A 到大写 Z 的字符。
[A-z]  查找任何从大写 A 到小写 z 的字符。
[adgk]  查找给定集合内的任何字符。
[^adgk]  查找给定集合外的任何字符。
(red|blue|green)  查找任何指定的选项。(可匹配单词)

3.2.4.5 元字符(Metacharacter)——拥有特殊含义的字符

.  查找单个字符,除了换行和行结束符。
\w  查找单词字符。(数字、字母、下划线)
\W  查找非单词字符。(除数字、字母、下划线的字符,包括中文、特殊符号、空格等)
\d  查找数字。
\D  查找非数字字符。
\s  查找空白字符。
\S  查找非空白字符。
\b  匹配单词边界。
\B  匹配非单词边界。
\0  查找 NUL 字符。
\n  查找换行符。
\f  查找换页符。
\r  查找回车符。
\t  查找制表符。
\v  查找垂直制表符。
\xxx  查找以八进制数 xxx 规定的字符。
\xdd  查找以十六进制数 dd 规定的字符。
\uxxxx  查找以十六进制数 xxxx 规定的 Unicode 字符。

3.2.4.6 量词

n+  =>  匹配任何包含至少一个 n 的字符串。
n*  =>  匹配任何包含零个或多个 n 的字符串。
n?  =>  匹配任何包含零个或一个 n 的字符串。
n{X}  =>  匹配包含 X 个 n 的序列的字符串。 a{2}  aa
n{X,Y}  =>  匹配包含 X 至 Y 个 n 的序列的字符串。a{2,4} aa aaa aaaa  
n{X,}  =>  匹配包含至少 X 个 n 的序列的字符串。a{2}  aa aaa aaaa aaaaa...
n$  =>  匹配任何结尾为 n 的字符串。
^n   => 匹配任何开头为 n 的字符串。
?=n   => 匹配任何其后紧接指定字符串 n 的字符串。
?!n   => 匹配任何其后没有紧接指定字符串 n 的字符串。

3.2.4.7 RegExp 对象方法

1. test => 检索字符串中指定的值。返回 true 或 false。

let reg = /[a-z]/i;
reg.test('A')   //true
reg.test('z')   //true
reg.test(1)    //false

2. exec => 检索字符串中指定的值。返回找到的值,并确定其位置。

let str = 'hello everybody!';
let reg = /e/i;
reg.exec(str)   //["e", index: 6, input: "hello everybody!", groups: undefined]
reg.exec('abc')    //null

3. compile => 编译正则表达式。用于改变 RegExp 对象
RegExpObject.compile(regexp,modifier)

let str = 'hello everybody!';
let reg = /e/i;
reg.compile('[0-9]', g)//reg: /[0-9]/g

3.2.4.8 支持正则表达式的 String 对象的方法

1. search => 检索与正则表达式相匹配的值(返回第一个匹配到的位置)

let str = 'hello everybody!';
let reg = /l/i;
str.search(reg) //2
reg = /f/g;
str.search(reg)  //-1

2. match => 找到一个或多个正则表达式的匹配。(返回匹配到的值)

let str = 'hello everybody!';
let reg = /l/g;
str.match(reg) //["l", "l"]
reg = /f/g;
str.match(reg)  //null

3. replace => 替换与正则表达式匹配的子串(返回替换后的整个字符串,改变原字符串)

let str = 'hello everybody!';
let reg1 = /l/;
str.replace(reg1, 'p')  //"heplo everybody!"
str = 'hello everybody!';
let reg2 = /l/g;
str.replace(reg2, 'p')  //"heppo everybody!"

4. split => 在符合 separator 的位置把字符串分割为字符串数组。

let str = 'hello everybody!';
let reg = /e/g;
str.split(reg)  //["h", "llo ", "v", "rybody!"]

3.2.5 Number、String、Boolean 构造函数

前面 2.1 基本类型 章节已经介绍了 Number、String、Boolean 类型,下面介绍下 Number、String、Boolean 三种构造函数。

3.2.5.1 Number 构造函数

  • 通过 Number 构造函数创建的是对象
let numObj = new Number(2);
let num = 2;

typeof numObj  //"object"
typeof num //"number"
numObj === num; //false

//通过 Number 构造函数创建的是对象可以调用数字的方法
let sum = numObj + numObj;  //4
let str = numObj + 'hello';  //"2hello"

//通过 Number 构造函数创建的是对象可以添加属性
numObj.name='jane'//
console.log(numObj.name)   //"jane"
num.name='tom'
console.log(num.name)  //undefined
  • Number() 也可以把各种值转换为数字,不能转换的就是 NaN。
Number('1')   //1
Number(true)  //1
Number(false)   //0
Number('text')  //NaN

3.2.5.2 String 构造函数

  • 通过 String 构造函数创建的是对象
let stringObj = new String('abc');
let string = 'abc';

typeof stringObj  //"object"
typeof string //"string"

stringObj === string; //false

//通过 String 构造函数创建的是对象可以添加属性
stringObj.name='jane'//
console.log(stringObj.name)   //"jane"
string.name='tom'
console.log(string.name)  //undefined

//通过 String 构造函数创建的是对象可以调用字符串的方法
let testStr = new String('testStr');
testStr.substr(1,3) //"est" 截取 testStr 从第1个开始,取3个
testStr.substring(1,4)  //"est" 截取 testStr 从第1个开始,到第4个,不包含第4个
testStr.slice(1,4)  //"est" 截取 testStr 从第1个开始,到第4个,不包含第4个
testStr.substring(2,0)  //"te"
testStr.slice(2,0)  //""
  • String() 也可以把各种值转换为字符串。
String(1)   //"1"
String(true)  //"true"
String(false)   //"false"
String('text')  //"text"
String([1,2,3])   //"1,2,3"
String({name: 'jane'})  //"[object Object]"

3.2.5.3 Boolean 构造函数*

通过 Boolean 构造函数创建的是对象

let booleanObj = new String(true);
let boolean = true;

typeof booleanObj  //"object"
typeof boolean //"boolean"

booleanObj === boolean; //false

//通过 Boolean 构造函数创建的是对象可以添加属性
booleanObj.name='jane'//
console.log(booleanObj.name)   //"jane"
boolean.name='tom'
console.log(boolean.name)  //undefined

  • Boolean() 也可以把各种值转换为布尔值,同 2.1 基本类型 里面介绍的一样 。
Boolean('111') === true
Boolean('text') === true//true
Boolean(2) === true//true
Boolean({}) === true//true
Boolean({name: 'jane'}) === true//true

3.2.6 Math

Math 不是对象的类,没有构造函数,但作为 JavaScript 内置的对象,可直接调用来进行数学方法的处理。

let num = 12345.12;
//四舍五入
Math.round(num);    //12345
//下舍入
Math.floor(num);    //12345
//上舍入
Math.ceil(num);    //12346
//取两个数中的最高值
Math.max(2,3)   //3
//取两个数中的最低值
Math.max(2,3)   //2
//返回 0 ~ 1 之间的随机数
Math.random()   //0.657487039270241

3.3 原型

所有引用类型其隐式原型都指向它的构造函数的显式原型

obj.__proto__ === Object.prototype

  • 当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,会去它的 __proto__ (即其构造函数的 prototype )中寻找。

隐式原型:

所有引用类型(数组、对象、函数),都有一个 __proto__ 属性,属性值是一个普通对象。

显式原型:

所有的函数都有一个 prototype 属性,属性值是一个普通对象。

hasOwnProperty:

只判断对象本身是否包含某属性,不去其原型链中寻找。

3.4 原型链

原型链结构图

当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,会去它的 __proto__ (即其构造函数的 prototype )中寻找,这在编程中被称为“原型继承”。

3.5 判断数据类型(构造函数、原型、typeof)

3.5.1 typeof

typeof——返回给定变量的数据类型,可能返回如下字符串:

返回字符串 —— 数据类型
'undefined' —— Undefined
'boolean' —— Boolean
'string' —— String
'number' —— Number
'symbol' —— Symbol
'object' —— Object / Null (Null 为空对象的引用)
'function' —— Function
  • typeof 返回的类型都是字符串形式,需注意,例如:
alert(typeof 'abcd' === "string")   =>   true
alert(typeof 'abcd' === String)   =>    false
  • typeof 对于Array、RegExp、Date 类型统一返回 'object'
alert(typeof [] === 'object')   =>    true
alert(typeof new Date)   =>    'object'
  • typeof 对于 function 类型统一返回 'function'
alert(typeof function(){} === 'function')   =>    true
  • typeof 是操作符而非函数,所以可以使用圆括号,也可以不使用。
alert(typeof 'abcd'); /     alert(typeof ('abcd'));

3.5.2 instanceof

判断一个函数是否是变量的构造函数
语法: objectA instanceof constructorB
判断逻辑: 变量a的 _ proto _ 一层一层往上找,用来检测 constructor.prototype 是否存在于参数 object 的原型链上,是则返回 true,不是则返回 false。

    alert([1,2,3] instanceof Array) ---------------> true
    alert(new Date() instanceof Date) ------------> true
    alert(function(){this.name="22";} instanceof Function) ------------> true
    alert(function(){this.name="22";} instanceof function) ------------> false

3.5.3 constructor

返回对象对应的构造函数

alert(({}).constructor === Object);  =>  true
alert(([]).constructor === Array);  =>  true
alert('abcde'.constructor === String);  =>  true
alert((1).constructor === Number);  =>  true
alert(true.constructor === Boolean);  =>  true
alert(false.constructor === Boolean);  =>  true
alert((function s(){}).constructor === Function);  =>  true
alert(new Date().constructor === Date);  =>  true
alert(new Array().constructor === Array);  =>  true
alert(new Error().constructor === Error);  =>  true
alert(document.constructor === HTMLDocument);  =>  true
alert(window.constructor === Window);  =>  true
alert(Symbol().constructor === Symbol);    =>    true
null 和 undefined 是无效的对象,没有 constructor,因此无法通过这种方式来判断。

注:

  • constructor 不能用来判断 Null 及 Undefined 类型
  • 函数的 constructor 不稳定。
    当一个函数被定义时,JS 引擎会为其添加 prototype 原型,然后在 prototype 上添加一个 constructor 属性,并让其指向函数的引用。
    但函数的 prototype 被重写后,原有的 constructor 引用会丢失。再次新建一个次函数的实例后,其 constructor 指向的内容已发生改变。
    因此为了规范开发,在重写对象原型时,一般都需要重新给 constructor 赋值,以保证对象实例的类型不被更改。

3.5.4 Object.prototype.toString()

  • toString() 是 Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]] 。
  • 这是一个内部属性,其格式为 [object Xxx] ,是一个字符串,其中 Xxx 就是对象的类型。
  • 对于 Object 对象,直接调用 toString() 就能返回 [object Object] 。而对于其他对象,则需要通过 call / apply 来调用才能返回正确的类型信息。
Object.prototype.toString.call(new Date) === '[object Date]';   //true
Object.prototype.toString.call(new String) === '[object String]';   //true
Object.prototype.toString.call(Math) === '[object Math]';   //true
Object.prototype.toString.call(undefined) === '[object Undefined]';   //true
Object.prototype.toString.call(null) ==='[object Null]';   //true
Object.prototype.toString.call('') === '[object String]' ;      //true 
Object.prototype.toString.call(123) === '[object Number]' ;       //true
Object.prototype.toString.call(true) === '[object Boolean]' ;    //true 
Object.prototype.toString.call(Symbol()) === '[object Symbol]';    //true
Object.prototype.toString.call(new Function()) === '[object Function]';    //true
Object.prototype.toString.call(new Date()) === '[object Date]' ;    //true
Object.prototype.toString.call([]) === '[object Array]';   //true
Object.prototype.toString.call(new RegExp()) === '[object RegExp]' ;    //true
Object.prototype.toString.call(new Error()) === '[object Error]';   //true
Object.prototype.toString.call(document) === '[object HTMLDocument]';   //true
Object.prototype.toString.call(window) === '[object Window]';   //true

类型判断小结:

  • 1)typeof 更适合判断基本类型数据,因为对于引用类型数据,typeof 只会返回 ‘function’ 或 ‘object’,不会返回其他的数组等类型;
  • 2)instanceof 只能用来判断实例类型,包括 Array、Date 等;
  • 3)constructor 不能用来判断 Null 及 Undefined 类型
  • 4)注:new String()、new Number() 生成的实际上为对象,但只能通过 typeof 能判断出来,constructor 和 .toString 只会返回 String 或 Number,无法判断是基本类型或是引用类型。

3.6 模拟面向对象的类

面向对象的编程中的类: 是用于创建对象的可扩展的程序代码模版,它为对象提供了状态(成员变量)的初始值和行为(成员函数和方法)的实现。

ES6 中封装了 class,在 ES6 中的 class 出现前,也可以通过其他的编程模式来创建类。

3.6.1 基于构造函数的类

function Person(name, age) {
    function getAge() {
        return age + 10;
    } 
    this.outputInfo = function() {
        console.log('name:', name, 'currentAge: ', getAge())
    }
}
let jane = new Person('jane', 14);
jane.outputInfo()     //name: jane currentAge:  24
  • 优点:
    如上,getAge 方法是外部不可见的(私有的),可隐藏内部方法的实现;而 outputInfo 被分配到 this 上,则是外部可见的(即公有的),用此构造函数创建的对象可以使用共有方法。

  • 缺点:
    所有的方法都在构造函数内部,每次 new 一个实例对象,都会创建内部的这些方法,且不同实例对象间不能共享这些方法,浪费内存资源。

3.6.2 基于原型的类

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype._getAge = function() {
    return this.age + 10;
}

Person.prototype.outputInfo = function() {
    console.log('name:', this.name, 'currentAge: ', this._getAge())
}
let jane = new Person('jane', 14);
jane.outputInfo()     //name: jane currentAge:  24
  • 优点:
    如上,将公共方法放在原型中,创建新对象直接调用原型中的方法,对象中没有的属性和方法,可以在原型链中寻找。

3.7 ES6 中的 class

在 JavaScript 中,class 实际上是一种“语法糖”,我们可以使用 class 去干净整洁地定义基于原型的类。

3.7.1 class 使用

3.7.1.1 基于原型实现类

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype._getAge = function() {
    return this.age + 10;
}

Person.prototype.outputInfo = function() {
    console.log('name:', this.name, 'currentAge: ', this._getAge())
}
let jane = new Person('jane', 14);
jane.outputInfo()     //name: jane currentAge:  24

3.7.1.2 基于 class 实现相同的类

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getAge () {
        return this.age + 10;
    }
    outputInfo () {
        console.log('name:', this.name, 'currentAge: ', this.getAge())
    }
}
let jane = new Person('jane', 14);
jane.outputInfo()     //name: jane currentAge:  24

3.7.2 class 完成了如下过程:

  • 声明了 Person 变量,且将它的值指向 constructor 函数,如下图:
Person
  • 所有类中定义的方法挂到了 Person.prototype 上,如下图:
Person.prototype

3.7.3 class 特点:

  • 必须与 new 一同使用,如下图:
without new
  • class 拥有一个默认的 constructor(){}
    class 中没有定义 constructor 的话,会默认生成一个空的构造函数:constructor(){}

  • class 中的方法都是不可枚举的
    这样遍历新建的对象时,类上的方法不会被遍历。

  • class 中只能定义方法

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    subName: 'jane'//只能定义方法
}
//Uncaught SyntaxError: Unexpected identifier
//如果一定要添加可在原型链上添加或通过 getter 添加

3.7.4 静态方法

新增在 class 上而非新增在 prototype 上的方法,称为静态方法。

3.7.4.1 定义静态方法

class Person {
    static sum(a, b){
        console.log(a+b)
    }
}

3.7.4.2 静态方法调用

静态方法需直接通过该类来调用,无需实例化,实例化调用会报错

class Person {
    static sum(a, b){
        console.log(a+b)
    }
}
Person.sum(1, 2);//3
let person = new Person();
person.sum(1, 2);//报错:Uncaught TypeError: person.sum is not a function

3.7.5 类继承

3.7.5.1 类继承的实现

假设现在有两个类:Person 和 Student

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getAge () {
        return this.age + 10;
    }
    outputInfo () {
        console.log('name:', this.name, 'currentAge: ', this.getAge())
    }
}

class Student {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getAge () {
        return this.age + 10;
    }
    outputInfo () {
        console.log('name:', this.name, 'currentAge: ', this.getAge())
    }
    startStudy () {
        alert(`我是${this.name}, 我要学习啦!`)
    }
}
let student = new Student('jane', 12)
student.startStudy()

如上,两个类是独立的,但其实 Student 是属于 Person 的,Person 中的方法 Student 中都有,且可以自己进行拓展,所以我们可以使用 “extends” 让 Student 来“继承” Person:

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getAge () {
        return this.age + 10;
    }
    outputInfo () {
        console.log('name:', this.name, 'currentAge: ', this.getAge())
    }
}

class Student extends Person {
    startStudy () {
        alert(`我是${this.name}, 我要学习啦!`)
    }
}
let student = new Student('jane', 12)
student.startStudy()

如上,便实现了 Student 对 Person 的继承,如果在 Student.prototype 中没有找到某个方法,就会从 Person.prototype 中继续寻找。

3.7.5.2 super 的作用

1. 重写构造函数 —— 继承父类参数

当子类有构造函数时,必须调用 super 方法来继承父类的参数。

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getAge () {
        return this.age + 10;
    }
    outputInfo () {
        console.log('name:', this.name, 'currentAge: ', this.getAge())
    }
}

class Student extends Person {
    constructor(name, age) {
        super(name, age); //继承父类参数,必须写在构造函数最前面
        this.studyStatus = false;   //必须写在 super() 之后,否则报错
    }

    startStudy () {
        super.outputInfo()
        alert(`我是${this.name}, 我要学习啦!`)
    }
}
let student = new Student('jane', 12)
student.startStudy()

2. 调用父类的方法 —— super.fun()

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getAge () {
        return this.age + 10;
    }
    outputInfo () {
        console.log('name:', this.name, 'currentAge: ', this.getAge())
    }
}

class Student extends Person {
    constructor(name, age) {
        super(name, age); //继承父类参数,必须写在构造函数最前面
        this.studyStatus = false;   //必须写在 super() 之后,否则报错
    }

    startStudy () {
        super.outputInfo()  //会执行父类的这个方法
        alert(`我是${this.name}, 我要学习啦!`)
    }
}
let student = new Student('jane', 12)
student.startStudy()

箭头函数没有自己的 super,它们的 super 即是就近的上下文的 super,同它的 this 一样。

3.7.5.3 重写父类方法

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    getAge () {
        return this.age + 10;
    }
    outputInfo () {
        console.log('name:', this.name, 'currentAge: ', this.getAge())
    }
}

class Student extends Person {
    constructor(name, age) {
        super(name, age); //继承父类参数,必须写在构造函数最前面
        this.studyStatus = false;   //必须写在 super() 之后,否则报错
    }
    outputInfo () {
        alert(`name: ${this.name}, currentAge:  ${this.getAge()}`)
    }
}
let student = new Student('jane', 12)
student.outputInfo()

如上,子类中有与父类相同的方法,会直接覆盖父类的方法,因为执行时,会现在 Student.prototype 中寻找这个方法,没有找到时,才会从 Person.prototype 中继续寻找。

参考:现代 JavaScript 教程

你可能感兴趣的:(JavaScript 知识梳理)