1. 创建对象的方式 (需要能够方便我们批量创建对象)
1. 字面量的方式
这个方式不合适, 不利于批量创建
let obj = {
name: 'QF001',
age: 18
}
2. 内置构造函数
这个方式不合适, 不利于批量创建
let obj = new Object()
let arr = new Array();
let time = new Date();
let reg = new RegExp();
3. 工厂函数的方式
其实就是 创建一个函数, 但是函数内部可以创建一个对象, 我们把这种函数叫做工厂函数
function createObj(name, age, height) {
let obj = new Object();
obj.name = name
obj.age = age
obj.height = height
return obj;
}
1) 对象可以通过 instanceof 来查看对象是否是属于哪一类
2) 语法: 对象 instanceof 构造函数
3) 返回值: true表示对象是属于这个构造函数这一类的,false表示不是构造
4. 自定义函数的书写
function Person(name, age) {
console.log(this)
this.name = name
this.age = age
this.a = '123'
}
new Person()
步骤解析:
p1 叫做 实例化对象, 实例对象, 本质上依然是一个对象
new Person() 这个过程叫做 构造函数的实例化, 最终会得到一个对象
1) 自定义函数注意点(面试必问)
1. 构造函数的函数名首字母 大写 (建议, 非强制), 目的就是为了和普通函数做一个区分
2. 构造函数内 不要写 return
如果 return 的是一个 基本数据类型, 写了也没用
return '字符串123'
返回值依然为:{name: 'QF666', age: 18}
如果 return 的是一个 引用数据类型, 写了就会导致构造函数没用
返回值:{t: '字符串123'}
3. 构造函数调用时, 必须和 new 关键字连用
如果不连用, 也能调用, 但是构造函数就没用了
4. 构造函数内部的 this(重要回答点)
当一个函数 和 new 关键字连用的时候, 那么我们说这个函数是 构造函数
然后这个函数内部的 this 指向本次调用 被自动创建出来的 那个对象
5. 构造函数不能使用 箭头函数
因为箭头函数内部没有 this
2) 构造函数的缺点
构造函数内部 如果有这个引用数据类型, 比如函数
在多次调用构造函数时, 每一次都会重新创建一个函数, 必然会造成
这个内存空间的浪费
function Person(name, age) {
this.name = name
this.age = age
this.a = '字符串123'
this.fn = function () {
console.log('我是 fn 函数')
}
}
new Person('QF001', 99)
new Person('QF001', 99)
第一次调用 Person 构造函数
自动创建一个对象
1. 添加一个属性 name, 值为形参 name
2. 添加一个属性 age, 值为形参 age
3. 添加一个属性 a, 值为一个固定的字符串
4. 添加一个属性 fn, 值为一个函数,
此时的函数是我们在这个函数内部定义的一个函数, 假设地址为 GD001
自动返回一个对象
第二次调用 Person 构造函数
自动创建一个对象
1. 添加一个属性 name, 值为形参 name
2. 添加一个属性 age, 值为形参 age
3. 添加一个属性 a, 值为一个固定的字符串
4. 添加一个属性 fn, 值为一个函数,
此时的函数是我们在这个函数内部定义的一个函数, 假设地址为 GD002
自动返回一个对象
因此: 两个对象内部的 fn 函数不是同一个内存地址
2. 原型 (又名: 原型对象 原型空间)
什么是原型?
1. 每一个函数 天生拥有一个属性 prototype, 他的属性值是一个 对象;
2. 我们通常把这个对象叫做 这个函数的原型(空间|对象)
3. 原型这个对象中 有一个属性 叫做 constructor, 这个属性表示的是: 当前这个 原型 是那个 函数的原型
原型的作用
把构造函数中 公共方法 提取出来, 放在原型中
为什么要这样做?
构造函数的原型上的方法或者属性, 在每一个实例化对象中都能正常访问
让实例对象 可以直接访问 原型对象中的方法属性(也叫作方法)
function Person(name) {
this.name = name;
}
Person.prototype.eat = function () {
console.log( '字符串123' );
}
let p1 = new Person('zs');
p1.eat()
构造函数 的prototype属性 指向 自己的原型对象
实例对象 的__proto__属性 指向 自己构造函数的原型对象
原型对象 的constructor属性 指向 自己的构造函数

3. 原型链
对象 通过 __proto__一直链接起来的一个链状结构,就是原型链
1) 原型链的访问规则 (背诵)
每个对象都会在其内部初始化一个属性,就是__proto__,当我们访问一个对象的属性时
先在对象本身查找,如果有则使用
如果没有,则去该对象__proto__指向的原型对象中查找,如果有则使用
如果还是没有找到,则沿着__proto__属性到下一个原型对象中查找,找到了就使用
直到查找到 JS 的顶层对象 Object.prototype(顶级构造函数 Object 对应的原型对象
没有 __proto__属性), 所以如果还没找到 就不再向上查找
2)原型链-经典面试题 必会
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// const fn = new Function('console.log(123)')
//函数最初的样子,但现在不这样写了=在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
// fn()
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function () {
console.log('你好~~~')
}
const p1 = new Person('QF001', 18)
/**
* 1. p1 的 __proto__ 指向谁
* __proto__ 指向自己构造函数的原型
* 所以 相当于 指向了 Person.prototype
*
* p1.__proto__ === Person.prototype
*
* 2. Person 的 __proto__ 指向谁
* Person 是一个构造函数, 构造函数本质上就是一个函数而已
* 在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
* __proto__ 指向自己构造函数的原型
* 所以相当于 指向了 Function.prototype
*
* Person.__proto__ === Function.prototype
*
* 3. Person.prototype 的 __proto__ 指向谁
* Person.prototype 是 Person 构造函数的原型, 本质上就是一个对象而已
* 在 JS 中, 只要是一个对象, 那么就属于构造函数 Object 的实例化对象
* __proto__ 指向自己构造函数的原型
* 所以相当于指向了 Object.prototype
*
* Person.prototype.__proto__ === Object.prototype
*
* 4. Function 的 __proto__ 指向谁
* Function 是一个构造函数, 构造函数本质上就是一个函数而已
* 在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
* __proto__ 指向自己构造函数的原型
* 所以相当于 指向了 Function.prototype
*
* Function.__proto__ === Function.prototype
*
* 5. Function.prototype 的 __proto__ 指向谁
* Function.prototype 是 Function 构造函数的原型, 本质上就是一个对象而已
* 在 JS 中, 只要是一个对象, 那么就属于构造函数 Object 的实例化对象
* __proto__ 指向自己构造函数的原型
* 所以相当于指向了 Object.prototype
*
* Function.prototype.__proto__ === Object.prototype
*
* 6. Object 的 __proto__ 指向谁
* Object 是一个构造函数, 构造函数本质上就是一个函数而已
* 在 JS 中, 只要是一个函数, 那么就属于构造函数 Function 的实例化对象
* __proto__ 指向自己构造函数的原型
* 所以相当于 指向了 Function.prototype
*
* Object.__proto__ === Function.prototype
*
* 7. Object.prototype 的 __proto__ 指向谁
* 因为 Object.prototype 是 JS 的顶层对象, 在往上就没有了, 所以这个值为 null
* console.log(Object.prototype.__proto__) null
*/
console.log(p1.__proto__ === Person.prototype)//true
console.log(Person.__proto__ === Function.prototype)//true
console.log(Person.prototype.__proto__ === Object.prototype)//true
console.log(Function.__proto__ === Function.prototype)//true
console.log(Function.prototype.__proto__ === Object.prototype)//true
console.log(Object.__proto__ === Function.prototype)//true
console.log(Object.prototype.__proto__)//null
</script>
</body>
</html>
4. 给内置构造函数添加方法
内置构造函数:Object() Array() RegExp() String()
在 JS 中, 任何一个数组, 他的构造函数都是 Array
在 JS 中, 任何一个对象, 他的构造函数都是 Object
案例: 在 数组上扩展一个方法 getMax,求出数组中的最大值
let arr = [1, 2, 3, 4, 5]
Array.prototype.getMax = function () {
console.log(this)
let max = this[0]
for (let i = 1; i < this.length; i++) {
if (this[i] > max) {
max = this[i]
}
}
console.log(max)
}
arr.getMax()