JS面向对象
- 对象与面向过程
面向过程就是亲力亲为,事无巨细,面面俱到,步步紧跟,有条不紊
面向对象就是找一个对象,指挥得结果
面向对象将执行者转变成指挥者
面向对象不是面向过程的替代,而是面向过程的封装
面向对象的特征:
封装性,继承性,多态性
构造函数和实例化对象的关系
(1)、解析构造函数代码的执行
创建一个实例对象,必须使用 new 操作符。以这种方式调用构造函数会经历以下 4 个步骤:
1、创建一个新对象
2、将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
3、执行构造函数中的代码
4、返回新对象
constructor属性
构造函数实例化对象的constructor属性指向的是构造函数本身
instanceof 关键字
检验是否为对象
构造函数的问题
内存浪费问题
使用构造函数带来的最大的好处就是创建对象更方便了,但是其本身也存在一个浪费内存的问题
对于每一个实例对象,如果我们在一个实例对象的内部创建一个属性,值为函数。假如创建两个对象,属性名也许一致,看似都是一模一样的内容,但是其实每一次生成一个实例,都会多占用一些内存,如果实例对象很多,会造成极大的内存浪费。
利用把属性值定义在外面方法,我们可以解决数据共享的问题,但是如果有多个需要共享的函数的话就会造成全局命名空间冲突的问题。
你肯定想到了可以把多个函数放到一个对象中用来避免全局命名空间冲突的问题:
至此,我们利用自己的方式基本上解决了构造函数的内存浪费问题。但是代码看起来还是那么的格格不入,那有没有更好的方式呢?
用原型来解决内存浪费问题
prototype
Javascript 规定,每一个构造函数都有一个 prototype 属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这也就意味着,我们可以把所有对象实例需要共享的属性和方法直接定义在 prototype 对象上。
构造函数 实例 原型
任何函数都具有一个 prototype 属性,该属性是一个对象。
构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数。
通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针
__proto__。
__proto__ 是非标准属性。
实例对象可以直接访问原型对象成员。
总结:
1、任何函数都具有一个 prototype 属性,该属性是一个对象
2、构造函数有一个protoType属性,它本身是一个对象,我们称之为原型
3、构造函数的protoType原型对象的属性和方法,都可以被构造函数实例化的对象所继承
4、构造函数的protoType原型对象有个constructor属性,指向的是当前原型对象所在的构造函数
5、实例对象有__proto__属性,它是一个指针,指向的是构造函数的的原型prototype
6、实例对象都具有一个 constructor 属性,指向创建该实例的构造函数
实例化对象的查找规则
先在自己身上找,找到即返回,自己身上找不到,则沿着原型链向上查找,找到即返回,如果一直到原型链的末端还没有找到,则返回 undefined
原型链
__proto__最终指向null
改变this指向的方法
(1)call()
1、call()方法可以进行普通函数的调用
2、call()方法可以改变this的指向,如果没有参数,this指向window
3、call()方法可以改变this的指向,如果有一个参数,this指向该参数
4、call()方法可以改变this的指向,如果有多个参数,this指向第一个参数,剩下的是个参数列表(构造函数继承的案例)
(2)apply()
1、 apply()方法可以进行普通函数的调用
2、apply()方法可以改变this的指向,如果没有参数,this指向window
3、apply()方法可以改变this的指向,如果有一个参数,this指向该参数
4、apply()方法可以改变this的指向,如果有多个参数,第一个参数是null或者window,第二个参数是数组
(3)bind()()
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。当目标函数被调用时 this 值绑定到 bind() 的第一个参数,该参数不能被重写。绑定函数被调用时,bind() 也接受预设的参数提供给原函数。一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。
1、bind()不能进行函数的调用
2、可以改变this指向