JavaScript对象(二)
本篇,主要讲了面向对象、this的指向问题,模拟继承过程
面向对象编程
- 什么面向对象编程?
- 编程,编程就是人们用计算机能懂的语言,告诉计算机自己想做的事情。
- 面向对象的编程的主要思想是把构成问题的各个事物
分解成各个对象
,建立对象的目的不是为了完成一个步骤,而是为了描述一个事物在解决问题的过程中经历的步骤和行为
。对象作为程序的基本单位,将程序和数据封装其中,以提高程序的重用性,灵活性和可扩展性
ps:在学习其它的语言,比如c语言,是面向过程的编程思想,我们现在了解一下,面向对象重点关注的是谁能帮我解决问题
,面向过程重点关注的是解决问题的过程
。
面向对象的三大特点
- 封装。
- 把资源整合
- 继承
- 使用继承可以用来复用代码。子类拥有了父类方法,以及重写
- 多态
- 一种事物的多种形态。分运行时多态(比如方法重写),编译时多态(比如方法重载)
创建对象
创建对象,主要可以分为两类;
- 工厂创建
- 构造函数
工厂创建
//举一个最简单的工厂创建例子
//需要一个对象,直接工厂创建,调用方法就行
var p = createPerson()
function createPerson( name , age ){
var obj = {}
obj.name = name
obj.age = age
return obj
}
ps:工厂创建,就是把创建对象权力交给了他人,自己就只需要把参数告诉工厂就可以了。
构造函数
//构造函数
function Person( name , age ){
this.name = name
this.age = age
this.say = fuction(){
...
}
}
//实例化一个对象
var p2 = Person()
注意:因为在JavaScript ES5里,就没有面向对象的写法,所以我们这上面的面向对象都是通过模拟来是实现的。在es6里,有class关键字,在类里写一个construct来构造。
prototype
我们有没有发现,在控制台打印一个function,把函数体展开就能看到一个prototype,这个prototype并不是我们自己定义的一个属性或者方法,那这个到底是什么呢?
- prototype叫做原型。创建fn函数自动获得prototype属性,该属性是一个对象即该函数的原型对象,我们可以看到原型对象默认会有一个constructor属性,该属性是指向函数自身即fn。
原型链
我们把_proto_展开,你会在展开后的最下面还能看到一个_proto_,再展开下一个,发现又有,直到你到Object对象的_proto_。不过这得嵌套了多少层了。其实我们可以理解,这样的_proto_好比是继承关系。a继承了b,b就是a的父亲,那么a里的_proto_指向的就是b。
应用:
比如你要给数组写一个方法,叫做myPop(),删除最后一个元素。
当你写完这个方法后,你可以通过原型方法,给一个数组对象Array叫上这个方法。Array.prototype.myPop = function(){...}
那么,只要是Array类型的变量,就都可以直接调用myPop()方法了
访问对象
- 用类似数组方式访问
var m = new Man('rainbow' , 20 )
访问name属性,console.log(m['name']) //rainbow
- for。。。in遍历每个成员
for ( const key in m ){
console.log( m[key] )
}
删除对象
var m = new Man('rainbow' , 20 )
//删除指定对象 , 执行成员
delete m1.name
ps:删除完后,Man对象就没有name这个属性了。
instance of
之前有个typeof,是用来检查数据类型。
- instance of是用来检查对象是否为某个对象的实例,返回true/false
e.g.
var m = new Man('rainbow' , 20 )
m instance of Man //true
m instance of Woman //false
m instance of Object //true,object为任何对象的祖先
hasOwnProperty
- 检查对象是否有自己的属性.返回true/false
var m = {
name : 'rainbwo',
age : 20
}
m.hasOwnProperty('name') //true
m.hasOwnProperty('sex') //flase
in
- 检查对象中是否有该属性和方法。返回true/false
var m = {
name : 'rainbwo',
age : 20
}
'name' in m //true
'age2' in m //false
'toString()' in m //true
ps: 对比hasOwnProperty、in
- hasOwnProperty自定义的。in自定义的+继承的
- hasOwnProperty只能检测当前对象中独有的属性和方法。in不仅仅是当前的,也能检测到所有继承而来的属性和方法。
this关键字
this关键字我们不陌生,之前在工厂创建对象时。用到了
this.name = name
this.age = age
-
其实,这个this指的就是当前的对象,使用this可以不用把调用者本身全部拼写出来,并且易于阅读。
-
但是,实际上,我们的this指向,并没有那么容易。
比如:
-
在在全局中,console.log(this). 这个this打印的Window,也叫程序上下文。
-
在一个方法内部(箭头函数除外),console.log(this),这个this指的就是函数所属的对象
-
箭头函数的this指向,Window
-
在事件函数中,this指向事件本身。
-
setTimeout,setInterval的this指向Window
改变this指向的三种方法
Function.prototype.xxx()
- call()
- apply()
- bind()
//定义test函数
function test(attr1,attr2,attr3) {
this.attr1 = attr1
this.attr2 = attr2
this.attr3 = attr3
}
//实例化一个对象
var obj = new Object(a , b, c);
-
test.call( obj , 1 , 2 , 3 )
call直接把this指向了obj
-
test.apply( obj ,[1 , 2 , 3])
apply指向了obj,并且用一个数组来传参数
-
test.bind( obj ).(1 , 2 , 3)
test.bind()把this指向了obj,并且返回了一个新的对象
ps:在JavaScript的语法里,就只有这三种方法可以改变this的指向
继承
继承就是让子类拥有父类的属性和方法
在es5里,没有继承的语法 ,es6有
所以我们可以来模拟一下
//父类
function Person(name , age ) {
this.name = name
this.age = age
}
//子类
Man 、 Woman
function Man( name , age , job ){
//构造函数的伪装,也就是通过this,改变指向
Person.call(this , name , age )//等价于,调用了Persion()
this.job = job
}
ps:
- 构造函数的伪装,其实就是继承了父类的属性和方法,用this指向的改变,推荐使用call()
- 但是,这样还不够,这里继承不完全。当我们使用instance of 检测,会找不到继承的属性以及方法。因此还需要最关键的一步
Man.prototype = Person.prototype
上面这一步叫做,原型继承。这样才算是真正的继承了。
谢谢大家阅读,鄙人知识、能力不足,如果讲的不好或者不正确的地方,还请原谅。支持原创