创建对象的三种方式
- 使用对象字面量
- 通过
new Object()
创建对象
1 2
| const obj = new Object() obj.name = 'Tom'
|
- 通过构造函数创建对象
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,返回值是新创建的对象。
❓实例化执行过程
- 创建新对象
- 构造函数this指向新对象
- 执行构造函数代码,修改this,添加新的属性
- 返回新对象
实例成员&静态成员
- 实例成员: 通过构造函数创建的对象称为实例对象,实例对象中的属性和方法称为实例成员。
- 静态成员:构造函数的属性和方法称为静态成员,通过构造函数直接访问。
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)
|
内置构造函数
基本数据类型,比如String、Number、Boolean等也都有专门的构造函数,称为包装类型,JS中几乎所有的数据都可以通过构造函数创建。
Object构造函数
Object.keys()
:返回对象的所有属性,返回值是一个数组。
Object.values()
:返回对象的所有属性值,返回值是一个数组。
1 2 3
| const obj = {name: 'Tom', age: 18} console.log(Object.keys(obj)) console.log(Object.values(obj))
|
Object.assign()
:拷贝对象,被拷贝的放在第二个参数,拷贝的放在第一个参数。使用场景为追加属性。
1 2 3 4
| const obj = {name: 'Tom', age: 18} const obj2 = {} Object.assign(obj2, obj) console.log(obj2)
|
Array构造函数
1
| const arr = new Array(3, 5)
|
forEach()
:遍历数组,不返回数组,经常用于查找遍历数组元素
1 2 3 4
| const arr = [1, 2, 3, 4, 5] arr.forEach((item, index) => { console.log(item, index) })
|
filter()
:过滤数组,返回一个新数组,返回的是筛选满足条件的数组元素
1 2 3
| const arr = [1, 2, 3, 4, 5] const newArr = arr.filter(item => item > 2) console.log(newArr)
|
map()
:遍历数组,返回一个新数组,返回的是处理之后的数组元素。
1 2 3
| const arr = [1, 2, 3, 4, 5] const newArr = arr.map(item => item * 2) console.log(newArr)
|
reduce()
:累加数组元素,返回累计处理的结果,经常用于求和。
没有初始值的时候,prev是数组的第一个元素,cur是数组的第二个元素,遍历次数是数组长度减一,有初始值的时候,prev是初始值,cur是数组的第一个元素,遍历次数是数组长度。
1 2 3
| const arr = [1, 2, 3, 4, 5] const sum = arr.reduce((prev, cur) => prev + cur, 0) console.log(sum)
|
join()
:将数组元素连接成一个字符串,返回一个字符串。
1 2 3
| const arr = [1, 2, 3, 4, 5] const str = arr.join('-') console.log(str)
|
find()
:查找数组元素,返回第一个满足条件的元素,找到就返回,没有符合条件的就返回undefined。
1 2 3
| const arr = [1, 2, 3, 4, 5] const res = arr.find(item => item > 2) console.log(res)
|
every()
:判断数组元素是否都满足条件,返回布尔值。
1 2 3
| const arr = [1, 2, 3, 4, 5] const res = arr.every(item => item > 2) console.log(res)
|
some()
:判断数组元素是否有一个满足条件,返回布尔值。
1 2 3
| const arr = [1, 2, 3, 4, 5] const res = arr.some(item => item > 2) console.log(res)
|
concat()
:合并数组,返回一个新数组。
1 2 3 4
| const arr = [1, 2, 3] const arr2 = [4, 5, 6] const newArr = arr.concat(arr2) console.log(newArr)
|
sort()
:排序数组,返回一个新数组。
splice()
:删除、替换、添加数组元素,返回一个新数组。
1 2 3
| const arr = [1, 2, 3, 4, 5] const newArr = arr.splice(1, 2) console.log(newArr)
|
reverse()
:反转数组,返回一个新数组。
1 2 3
| const arr = [1, 2, 3, 4, 5] const newArr = arr.reverse() console.log(newArr)
|
findIndex()
:查找数组元素的索引,返回第一个满足条件的元素的索引,找到就返回,没有符合条件的就返回-1。
1 2 3
| const arr = [1, 2, 3, 4, 5] const index = arr.findIndex(item => item > 2) console.log(index)
|
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)
|
String构造函数
length
:字符串长度。
split('分隔符'')
:将字符串分割成数组,返回一个数组。
1 2 3
| const str = 'hello world' const arr = str.split(' ') console.log(arr)
|
substring(需要截取的第一个字符的索引,结束的索引号)
:截取字符串,返回一个新字符串。
1 2 3
| const str = 'hello world' const newStr = str.substring(0, 5) console.log(newStr)
|
startsWith('检测字符串', 检测位置索引号)
:判断字符串是否以某个字符串开头,返回布尔值。
1 2 3
| const str = 'hello world' const res = str.startsWith('hello') console.log(res)
|
includes('检测字符串', 检测位置索引号)
:判断字符串是否包含某个字符串,返回布尔值。
1 2 3
| const str = 'hello world' const res = str.includes('hello') console.log(res)
|
toUpperCase()
:将字符串转换为大写。
toLowerCase()
:将字符串转换为小写。
indexOf()
:查找字符串,返回索引号。
endsWith()
:判断字符串是否以某个字符串结尾,返回布尔值。
replace('被替换的字符串', '替换的字符串')
:替换字符串,返回一个新字符串。支持正则匹配
1 2 3
| const str = 'hello world' const newStr = str.replace('hello', 'hi') console.log(newStr)
|
match()
:查找字符串,返回一个数组。支持正则匹配
Number构造函数
- toFixed():保留小数位数
1 2
| const num = 3.1415926 console.log(num.toFixed(2))
|
构造函数存在浪费内存的问题
- 构造函数中如果有方法,每次实例化都会在堆上创建一个方法对象,浪费内存
- 如果希望所有的对象使用同一个函数来节省内存,可以使用原型
原型 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 }
|
❓为什么实例对象可以访问原型对象里的属性和方法呢?
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. 原型链
基于原型对象的继承使得不同构造函数的原型对象关联在一起,并且这种关联的关系是一种链状的结构,我们将原型对象的链状结构关系称为原型链。

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