构造函数的问题
构造函数存在的问题
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.我们常说的指针/指向
// 表示的就是内存中的地址(门牌号/房间号),我们可以通过这个地址访问到内存中实际的数据
引入原型解决这个问题
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
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
构造函数-实例-原型之间的关系
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
// 实例与构造函数原型之间有直接的联系,但实例与构造函数之间没有。
原型链
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
})