详谈js之面向对象
前言
JS是一个基于对象的语言。既能面向过程又能面向对象。
- 封装
- 继承
- 多态
JS已有的对象的理解
new Array() , new String(), new Date(), new Number(), new Boolean(), new Function()
不管是什么对象,这个对象下一定有一个属性叫__proto__
现在大部分浏览器显示的都是[[Prototype]]
但是这个属性不能用,还是得用__proto__
前后各两个杠
let obj = {}
console.log(obj.__proto__)
let arr = []
console.log(arr.__proto__)
let str = new String('')
console.log(str.__proto__)
由引用值改变引出Prototype
let obj2 = {}
let obj1 = {
a: 1,
b: 2,
c: obj2
}
obj2.key = "value"
console.log(obj1) // {a: 1, b: 2, c: {key: "value"}}
prototype
原型对象,因为原型对象在对应的构造函数中,只有一个
所有对象的__proto__
都指向其对应的构造函数的prototype
let arr = [] // 他的构造函数Array
// 就可以得到
arr.__proto__ === Array.prototype // true
上面这个例子就像更上上面的obj1和obj2的关系一样
arr就是obj1, Array.prototype就是obj2
constructor
这个指的是构造函数,所有的通过new 得到的对象的prototype里都有这个属性,这个属性就是你new的时候后面的跟着的构造函数
这个属性在原型对象中
Array.prototype.constructor // Array
为什么要设计__proto__和prototype
正常情况下,我们如果给每个对象都添加相同的方法,会导致相同的方法在堆内存中占用大量的内容。其实没有必要,但是我们又没有办法解决这个问题。
js提出了原型对象的概念,可以把所有的公共方法全部放在一个对象,然后让原型对象和实例对象产生关联,然后可以节省内容
特性!!!当我们调用实例对象下的方法时,js会去实例对象中找方法,找到就调用。没找到就去对应的构造函数的原型对象中找。找到就调用,找不到继续往下找对应的构造函数的原型对象的构造函数的原型对象。直到没有找到,才会报错。
原型链
拿JS中自带的对象举例:
当我们生成一个数组时,数组是实例对象,Array是构造函数,Object构造函数
数组.__proto__就是Array.prototype
Array.prototype.__proto__就是Object.prototype
Object.__proto__就是null
特性!!!只要在原型链上的对象有某个方法,那么实例对象就能调用。
instanceOf
[] 通过 new Array得到,所以我们可以说 [] 是 Array的实例对象,Array是[]的构造函数,就会有以下关系
[] instanceof Array // true
{} instanceof Object // true
instanceof可以用来判断某个值是不是数组
可能出现的问题
假设我们想要当所有的数组都可以调用某个方法,我们需要怎么做。
可以把对应的方法设置到原型对象中
Array.prototype.方法名 = function () {}
封装
在面向对象的操作中,我们有完全不同的一些写法,需要学习。
想要封装我们的对象,就要用到构造函数。我们需要创建构造函数,构造函数和函数一致,都是通过function创建的
- 首字母大写(规范,为了和普通函数进行区分)
- 通过new调用的函数叫构造函数,new完之后得到的结果叫实例对象
属性写在实例对象中,方法写在原型对象中
function 构造函数名 (参数, 参数n) {
this.属性 = 参数
this.属性 = 值
}
构造函数.prototype.方法名 = function () {
}
构造函数.prototype.方法名2 = function () {
}
构造函数.prototype.方法名n = function () {
}
构造函数中的this指向new之后的实例对象。构造函数的原型对象中的方法里的this也指向实例对象。
我们可以在原型对象的方法中,直接通过this调用属性和方法。
function Person (name, age) {
this.name = name
this.age = age
// 方法写在构造函数中,每生成一个实例对象,都会在实例对象中生成一个新的方法,浪费内存。
/* this.say = function () {
console.log(`我叫${this.name},今年${this.age}岁`)
} */
}
Person.prototype.type = "人"
Person.prototype.say = function () {
// 原型对象的方法中,可以通过this调用其他方法 也可以通过this实现属性
console.log(`我叫${this.getName()},今年${this.age}岁`)
}
Person.prototype.getName = function () {
return this.name
}