Skip to content

构造函数的问题

构造函数存在的问题

image.png

javascript
function Person(name, age){
    this.name = name 
    this.age = age 
    this.sing = function(){
        console.log('hi~~')
    }
}
const zjl = new Person('zjl', 18)
const kk = new Person('kk', 20)

// zjl.sing()
// kk.sing()
console.dir(zjl)
console.dir(kk)
// 判断两个方法是否相等
console.log(zjl.sing === kk.sing)  // false 表示地址sing方法的地址不同

// 每创建一个对象,就会在堆内存中开辟一个空间存储方法
// 当我们创建特别多对象时,就会占用很多内存空间

// PS.我们常说的指针/指向
// 表示的就是内存中的地址(门牌号/房间号),我们可以通过这个地址访问到内存中实际的数据

引入原型解决这个问题

image.png

javascript
function Person(name, age){
    // 1. 公共的属性都放在构造函数里面
    this.name = name 
    this.age = age 
    // this.sing = function(){
    //     console.log('hi~~')
    // }
}
// 2. 公共的方法,写到原型对象上,节约了内存
Person.prototype.sing = function(){
    console.log('hi~~~')
}
const zjl = new Person('zjl', 18)
const kk = new Person('kk', 20)

console.dir(zjl)
console.dir(kk)
zjl.sing()
kk.sing()

console.log(zjl.sing === kk.sing) // true

// 以后,一般情况下
// 1. 公共的属性写到构造函数里面
// 2. 公共的方法写到原型对象上!!!

原型

原型prototype

image.png

javascript
// 原型: 原型的类型,原始祖先
// JS里面:原型就是一个对象,也叫原型对象
// 原型对象:Person.prototype 所指向的那个对象


// 1. 规则3: 所有的函数,都有一个prototype属性(显示原型),这个属性是一个指针,指向原型对象
function Person(){}
console.dir(Person)

// 2. 原型上默认有一个constructor的属性,指向这个构造函数本身  ==> 记下来
console.log(Person.prototype.constructor === Person)

// 3. 我们可以往这个原型上添加属性和方法
// ==> 所有通过构造函数创建的实例,都可以共享原型上的属性和方法
function Star(name, age){
    // 3.1. 公共的属性写在构造函数里面
    this.name = name 
    this.age = age
}
// 3.2. 公共的方法写到原型上
Star.prototype.eat = function(){
    console.log('冰淇淋')
}
// 3.3. 但是,原型上也是可以添加属性的
Star.prototype.cheer = 'Every Step Counts' // 每一步都有意义,每天进步一点点

const kk = new Star('kk', 18)
console.dir(kk)
kk.eat()
console.log(kk.cheer)

// 4. 规则4 所有对象的隐式原型指向构造函数的显示原型
console.log(kk.__proto__ === Star.prototype)

构造函数和原型中的this指向

javascript
// 1. 构造函数中的this指向 实例对象  new出来的那个对象
function Person(name){
    this.name = name 
    // console.log(this)
}

// 2. 原型对象中的this ==这里指的是原型方法中的this
Person.prototype.sayHi = function(){
    console.log('hi~~~')
    console.log(this)
}

const p = new Person('jl')
const mby = new Person('mby')

p.sayHi()
mby.sayHi()

// ==> 构造函数中的this和原型对象中的this,都是指向实例对象(new出来的那个对象)

constructor属性

javascript
function Star(name){
    this.name = name 
}
const ldh = new Star('刘德华')
console.log(ldh)

// 1. constructor在哪里?在原型上
// 2. 我们怎么找到原型对象?
//  ===> 1. Star.prototype  ==> 找到原型
//  ===> 2. ldh.__proto__   ==> 找到原型
console.log(ldh.__proto__ === Star.prototype)

// 3. 背下来

// 每一个原型(对象)都有一个constructor属性,指向构造函数本身
// ==> 表示,我,我这个原型,和哪个构造函数有关联,是哪个构造函数的原型
console.log(Star.prototype.constructor === Star)

constructor的应用

javascript
function Star(name){
    this.name = name 
}
// Star.prototype.sing = function(){
//     console.log('唱歌')
// }

// Star.prototype.dance = function(){
//     console.log('跳舞')
// }

// Star.prototype.rap = function(){
//     console.log('rap')
// }

// 如果我们直接给原型赋值一个对象,相当于整个替换了原型对象的所有内容
// 那么原型中的默认的constructor属性就丢失了,我们就不知道这个原型是哪个构造函数的原型了

// ==> 所以,我们可以手动利用constructor重新添加上去,指回去
Star.prototype = {
    constructor:Star,
    sing:function(){
        console.log('唱歌')
    },
    dance:function(){
        console.log('跳舞')
    },
    rap:function(){
        console.log('rap')
    }
}

const kk = new Star('kk')
console.dir(kk)
kk.sing()
kk.rap()
kk.dance()


let obj = {
    a: 1,
    b: 2,
    c: 3
}
obj.d = 4
// console.log(obj)

// 直接赋值一个对象,相当于把原来的全部覆盖了
obj = {f:1}
console.log(obj)

隐式原型__proto__

javascript
function Star(name){
    this.name = name 
}
Star.prototype.sing = function(){
    console.log('唱歌')
}
const ldh = new Star('刘德华')
console.log(ldh)

// 1. 规则2  所有的对象,都有__proto__(隐式原型),属性值是一个普通的对象
// 2. 规则4  所有对象的隐式原型(__proto__)都指向它的构造函数的显示原型(prototype)
// 3. 因为函数也是一个对象,所以函数也有__proto__属性,但是函数还有prototype属性
console.dir(Star)

// __proto__隐式原型
// __proto__相当于是一个桥梁,链接,实例可以通过__proto__访问到它的原型对象

// 我们说原型,可以说是某个构造函数的原型,也可以说是这个构造函数创建的实例 的 原型

// 4. 原型规则5 (对象方法属性的查找规则)
// 当试图得到对象的某个属性(方法)时,
// 如果这个对象本身没有这个属性(方法),我们就会通过__proto__在原型中寻找

原型小结-等式

javascript
function Animal(){
    // 相当于创建的实例,默认都有color属性,属性值也是固定
    this.color = 'orange'
}

const cat = new Animal()
console.log(cat)

// 1. 实例对象.__proto__ === 构造函数.prototype
console.log(cat.__proto__ === Animal.prototype)

// 2. 构造函数.prototype.constructor === 构造函数
console.log(Animal.prototype.constructor === Animal)
console.log(cat.__proto__.constructor === Animal)

// 3.返回指定实例(对象)的原型 ==> 了解
// Object.getPrototypeOf(obj)

// Animal.prototype  ==> 原型
// cat.__proto__     ==> 原型
console.log(Animal.prototype)
console.log(cat.__proto__)
console.log(Object.getPrototypeOf(cat)) // 原型

Object.getPrototypeOf(cat) === Animal.prototype
Object.getPrototypeOf(cat) === cat.__proto__

console.log(Object.getPrototypeOf(cat) === Animal.prototype) // true 
console.log(Object.getPrototypeOf(cat) === cat.__proto__)  // true

构造函数-实例-原型之间的关系

image.png

javascript
function Person(name){
    this.name = name 
}
const person = new Person('杰伦')

// 四条线
// 1. 构造函数,有prototype属性  指向原型对象
// 2. 原型默认有constructor属性,指回构造函数
// 3. 实例通过__proto__访问到原型
// 4. 构造函数new出了一个实例


// 1. 构造函数.prototype  ==> 原型
// 2. 原型.constructor   ==>  构造函数
// 3. 实例.__proto__     ==>  原型
// 4. 构造函数 ==> new 出实例

// 高程4 第八章 P250
// 实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有。

原型链

image.png

javascript
// 原型链的推导过程

// 1. Person.prototype也是一个对象 规则2.它也有一个__proto__属性 

// 实例对象.__proto  ===> 能访问到它自己的原型
// 我们把Person.prototype看作一个实例对象整体
// Person.prototype.__proto__ === >  

// Person.prototype既然是一个对象,它的原型又是谁呢?它的构造函数是谁呢?
// 实例对象通过__proto__能访问到它(这个原型Person.prototype)的原型

// Person.prototype这个对象的 是 ===> Object构造函数创建
// Person.prototype = { }   ==>   new Object()
// 规则4: 对象的隐式原型指向它的构造函数的显示原型
// 对象.__proto__ === 构造函数.prototype

Person.prototype.__proto__ === Object.prototype 

// 2. Object.prototype 得到的是什么,原型,谁的原型, Object构造函数的原型
// 2.1 从原型上找构造函数
Object.prototype.constructor === Object
// 2.2 从构造函数 找原型
Object.prototype  ==> 原型
// 2.3 刚才的
Person.prototype.__proto__  ==> 原型   访问到的还是Object的原型

// 3. Object.prototype 访问到的是Object的原型,也是一个对象
// Object.prototype  ==> 它指向Object的原型

// Object.prototype.__proto__ === 访问到的是它这个例(Object.prototype)的原型

// console.log(Object.prototype.__proto__ === null)

// ==> Object原型的原型是null

原型链-总结

javascript
/* ==================== 面试题 背下来 ===================== */
// 什么是原型链?
// 每个对象通过__proto__属性能访问到它的原型,原型也有它的原型。
// 当访问一个对象的属性和方法的时候,先在自身中寻找,
// 如果没有,就会沿着__proto__这条链,往上(在它的原型中)寻找,一直找到最顶层Object.prototype为止

// Object.prototype.__proto__ === null
/* ==================== end ===================== */

// 数组的原型链 
const arr = [1, 2, 3]    // new Array()
// 数组也是一个对象,它由Array这个构造函数创建
// arr.__proto__ === Array.prototype
arr ---> Array.prototype  --->  Object.prototype  ---> null
// __proto__ 是一个桥梁


// 函数的原型链
const fn = function(){} 
// 函数也是一个对象 它由Function构造函数创建
// fn.__proto__ === Function.prototype

fn ---> Function.prototype  ---> Object.prototype  ---> null

原型-总结

javascript
/* ==================== 什么是原型 ===================== */
// 原型就是一个对象
// 1. 每个函数都有prototype属性,它的值是一个指针,指向的就是原型对象
// 2. 通过构造函数创建的实例,都有一个__proto__属性,也指向原型对象
// 3. 原型上默认由一个constructor属性,指回构造函数

// 作用:我们可以把一些公共的属性和方法放到原型上,节约内存。
// ===> 所有通过构造函数创建的实例都共享原型上的属性和方法
/* ==================== end ===================== */

function A(){}
const a = new A()

a.__proto__ === A.prototype

// 原型的结构如下

// 通过构造函数访问
A.prototype = {
    constructor: A,
    // [[prototype]] / __proto__
    // 其他原型上的属性和方法
}

// 实例通过__proto__访问
a.__proto__ = {
    constructor: A,
    // [[prototype]] / __proto__
    // 其他原型上的属性和方法
}

instanceof

javascript
// instanceof 运算符    instance 实例 of 谁的
// 语法 : A instanceof B
//        实例 ----->  构造函数
// 作用: 用于检测构造函数的原型是否出现在某个实例对象的原型链上。
// 理解:
//  ===>用于检测A这个实例(对象)是不是B这个构造函数创建的,或者是不是B这个祖先构造函数创建的

const arr = [1, 2, 3]   // new Array()

console.log(arr instanceof Array) // true 
// Array.prototype.__proto__  === Object.prototype
console.log(arr instanceof Object) // true

// 数组的原型链
// arr ---> Array.prototype  ---> Object.prototype ---> null

function C(){
    this.name = 'orange'
}
function D(){
    this.name = 'hhh'
}
const o = new C()
console.dir(o)

console.log(o instanceof C) // o 是 C构造函数创建的
console.log(o instanceof D) // o 不是D创建的 或者说  D.prototype 不在 o的原型链上
console.log(o instanceof Object)  // Object是o的祖先构造函数

作业

javascript
const arr = [1,2,3]
const res = arr.filter(el => {
  // 1 >= 2  
  // return 1 >= 2 ==> return false 
  // return 2 >= 2 ==> reutrn true   2 这个元素留下来了
  // return 3 >= 2 ===> return true  3 这个元素留下来了
  return el >= 2
})