创建对象的三种方式

  1. 使用对象字面量
  2. 通过new Object()创建对象
1
2
const obj = new Object()
obj.name = 'Tom'
  1. 通过构造函数创建对象
1
2
3
4
5
6
7
function Person(name, age) {
this.name = name
this.age = age
}

const p = new Person('Tom', 18)
const p2 = new Person('Jerry', 20)
  • 构造函数中的this指向当前对象,通过new关键字调用构造函数时,this指向新创建的对象。
  • 构造函数的首字母大写,约定俗成。
  • 实例化构造函数时没有参数可以省略,不需要写return,返回值是新创建的对象。

❓实例化执行过程

  1. 创建新对象
  2. 构造函数this指向新对象
  3. 执行构造函数代码,修改this,添加新的属性
  4. 返回新对象

实例成员&静态成员

  • 实例成员: 通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员。
  • 静态成员:构造函数的属性和方法称为静态成员,通过构造函数直接访问。
1
2
3
4
5
6
7
8
9
function Person(name, age) {
}

Person.hobby = 'swim'
Person.eat = function () {
console.log('eat')
}

console.log(Person.hobby) // swim

内置构造函数

基本数据类型,比如String、Number、Boolean等也都有专门的构造函数,称为包装类型,JS中几乎所有的数据都可以通过构造函数创建。

Object构造函数

  1. Object.keys():返回对象的所有属性,返回值是一个数组。
  2. Object.values():返回对象的所有属性值,返回值是一个数组。
1
2
3
const obj = {name: 'Tom', age: 18}
console.log(Object.keys(obj)) // ['name', 'age']
console.log(Object.values(obj)) // ['Tom', 18]
  1. Object.assign():拷贝对象,被拷贝的放在第二个参数,拷贝的放在第一个参数。使用场景为追加属性。
1
2
3
4
const obj = {name: 'Tom', age: 18}
const obj2 = {}
Object.assign(obj2, obj)
console.log(obj2) // {name: 'Tom', age: 18}

Array构造函数

1
const arr = new Array(3, 5) // [3, 5]
  1. forEach():遍历数组,不返回数组,经常用于查找遍历数组元素
1
2
3
4
const arr = [1, 2, 3, 4, 5]
arr.forEach((item, index) => {
console.log(item, index)
})
  1. filter():过滤数组,返回一个新数组,返回的是筛选满足条件的数组元素
1
2
3
const arr = [1, 2, 3, 4, 5]
const newArr = arr.filter(item => item > 2)
console.log(newArr) // [3, 4, 5]
  1. map():遍历数组,返回一个新数组,返回的是处理之后的数组元素。
1
2
3
const arr = [1, 2, 3, 4, 5]
const newArr = arr.map(item => item * 2)
console.log(newArr) // [2, 4, 6, 8, 10]
  1. reduce():累加数组元素,返回累计处理的结果,经常用于求和。
    没有初始值的时候,prev是数组的第一个元素,cur是数组的第二个元素,遍历次数是数组长度减一,有初始值的时候,prev是初始值,cur是数组的第一个元素,遍历次数是数组长度。
1
2
3
const arr = [1, 2, 3, 4, 5]
const sum = arr.reduce((prev, cur) => prev + cur, 0) // 0是初始值
console.log(sum) // 15
  1. join():将数组元素连接成一个字符串,返回一个字符串。
1
2
3
const arr = [1, 2, 3, 4, 5]
const str = arr.join('-') // 1-2-3-4-5,默认是逗号
console.log(str)
  1. find():查找数组元素,返回第一个满足条件的元素,找到就返回,没有符合条件的就返回undefined。
1
2
3
const arr = [1, 2, 3, 4, 5]
const res = arr.find(item => item > 2)
console.log(res) // 3
  1. every():判断数组元素是否都满足条件,返回布尔值。
1
2
3
const arr = [1, 2, 3, 4, 5]
const res = arr.every(item => item > 2)
console.log(res) // false
  1. some():判断数组元素是否有一个满足条件,返回布尔值。
1
2
3
const arr = [1, 2, 3, 4, 5]
const res = arr.some(item => item > 2)
console.log(res) // true
  1. concat():合并数组,返回一个新数组。
1
2
3
4
const arr = [1, 2, 3]
const arr2 = [4, 5, 6]
const newArr = arr.concat(arr2)
console.log(newArr) // [1, 2, 3, 4, 5, 6]
  1. sort():排序数组,返回一个新数组。
  2. splice():删除、替换、添加数组元素,返回一个新数组。
1
2
3
const arr = [1, 2, 3, 4, 5]
const newArr = arr.splice(1, 2) // 2, 3
console.log(newArr) // [1, 4, 5]
  1. reverse():反转数组,返回一个新数组。
1
2
3
const arr = [1, 2, 3, 4, 5]
const newArr = arr.reverse()
console.log(newArr) // [5, 4, 3, 2, 1]
  1. findIndex():查找数组元素的索引,返回第一个满足条件的元素的索引,找到就返回,没有符合条件的就返回-1。
1
2
3
const arr = [1, 2, 3, 4, 5]
const index = arr.findIndex(item => item > 2)
console.log(index) // 2

Array.from()
用来把伪数组对象或可遍历对象转换为真数组。

1
2
3
4
5
6
7
8
const obj = {
0: 'a',
1: 'b',
2: 'c',
length: 3
}
const arr = Array.from(obj)
console.log(arr) // ['a', 'b', 'c']

String构造函数

  1. length:字符串长度。
  2. split('分隔符''):将字符串分割成数组,返回一个数组。
1
2
3
const str = 'hello world'
const arr = str.split(' ')
console.log(arr) // ['hello', 'world']
  1. substring(需要截取的第一个字符的索引,结束的索引号):截取字符串,返回一个新字符串。
1
2
3
const str = 'hello world'
const newStr = str.substring(0, 5)
console.log(newStr) // hello
  1. startsWith('检测字符串', 检测位置索引号):判断字符串是否以某个字符串开头,返回布尔值。
1
2
3
const str = 'hello world'
const res = str.startsWith('hello')
console.log(res) // true
  1. includes('检测字符串', 检测位置索引号):判断字符串是否包含某个字符串,返回布尔值。
1
2
3
const str = 'hello world'
const res = str.includes('hello')
console.log(res) // true
  1. toUpperCase():将字符串转换为大写。
  2. toLowerCase():将字符串转换为小写。
  3. indexOf():查找字符串,返回索引号。
  4. endsWith():判断字符串是否以某个字符串结尾,返回布尔值。
  5. replace('被替换的字符串', '替换的字符串'):替换字符串,返回一个新字符串。支持正则匹配
1
2
3
const str = 'hello world'
const newStr = str.replace('hello', 'hi')
console.log(newStr) // hi world
  1. match():查找字符串,返回一个数组。支持正则匹配

Number构造函数

  1. toFixed():保留小数位数
1
2
const num = 3.1415926
console.log(num.toFixed(2)) // 3.14

构造函数存在浪费内存的问题

  • 构造函数中如果有方法,每次实例化都会在堆上创建一个方法对象,浪费内存
  • 如果希望所有的对象使用同一个函数来节省内存,可以使用原型

原型 prototype

  • 构造函数通过原型分配的函数是所有对象共享的,JS规定,每一个构造函数都有一个prototype属性,指向另一个对象,也称为原型对象。
  • 这个对象可以挂载函数,对象实例化不会多次创建原型上函数,节省内存
  • 我们可以把不变的方法直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。
  • 构造函数和原型对象中的this都指向实例化的对象。

1. Constructor属性

  • 每个原型对象都有一个constructor属性,该属性指向该原型对象的构造函数
  • 如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象constructor就不指向当前构造函数了,这个时候可以在修改后的原型对象中,添加一个constructor指向原来的构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Person(name, age) {
this.name = name
this.age = age
}

Person.prototype = {
sing: function () {
console.log('sing')
},
dance: function () {
console.log('dance')
},
constructor: Person // 添加constructor指向构造函数
}

❓为什么实例对象可以访问原型对象里的属性和方法呢?

2. 对象原型

在每一个实例对象中都有一个属性__proto__,指向构造函数的原型对象,也就是Person.prototype

  • __proto__是JS非标准属性,与[[Prototype]]意义相同,都是用来表明当前实例对象指向哪个原型对象prototype
  • __proto__对象原型中也有一个constructor属性,指向创建该实例对象的构造函数
    三者关系图

3. 原型继承

JavaScript中大多是借助原型对象实现继承的特性。

子类的原型 = new 父类的实例对象

1
2
3
4
5
6
7
8
9
10
11
function Person() {
this.eye = 2
this.head = 1
}

function Student() {

}

Student.prototype = new Person()
Student.prototype.constructor = Student

4. 原型链

基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链。

原型链

原型链是查找规则:

  1. 当访问一个对象的属性(包括方法)的时候,首先查找这个对象自身有没有该属性。
  2. 如果没有就查找它的原型,也就是**proto**指向的对象。
  3. 如果还没有就查找原型对象的原型
  4. 以此类推一直找到Object为止。
  5. __proto__对象的原型的意义是为对象成员查找机制提供一个方向,或者说是一条路线
  6. 可以使用instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。