面向对象
是编程思想,JAVA
、PHP
、C#
、C++
、.net(dot net)
、JS
…这些都是面向对象编程的
面向过程
编程思想:C语言
是面向过程的
html、css
:是标记语言
不是编程语言,没有所谓的面向对象编程less、sass:属于css预编译语言,旨在把css变为编程语言(面向对象的编程语言)
面向对象
JS本身就是
基于面向对象创造出来的语言,而非是面向对象的语言
JS
可以模拟
实现继承
和封装
,但是无法模拟
实现多态
,所以我们说JS是一门基于对象的语言
,而非是面向对象的语言
。
JavaScript是通过原型(prototype)来实现面向对象编程
面向对象的三大特性:
1、封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式
,将变化隔离,便于使用,提高复用性和安全性
。
2、继承
提高代码复用性
;继承是多态的前提。
3、多态
父类或接口定义的引用变量可以指向子类或具体实现类的实例对象
。提高了程序的拓展性。多态就是通过传递给父类对象引用不同的子类对象从而表现出不同的行为,多态可为程序提供更好的可扩展性,同样也可以代码重用。因为js是弱类型语言,所以不能实现多态
对象、类、实例
对象
:编程语言中的对象是泛指,万物皆对象(我们所要研究学习以及使用的都是对象)
类
:是抽象的。对象的具体细分(按照属性或者特性细分为一些类别)
实例
:是具体的。某一类中具体的事物(对象数据类型)
实例就是一个对象
[实际生活当中]
+人
+正常人类
+程序员类
我就是程序员类别中的一个实例
JS中常用的内置类(内置类都是 函数)
- 关于数据类型的:
Number
:每一个数字或者NaN是它的一个实例String
:字符串类Boolean
:布尔类Null
Undefined
:浏览器屏蔽了我们操作Null或者Undefined这个类Object
:对象类,每一个对象数据类型都是它的实例Array
:数组类RegExp
:正则类Date
:日期类- …
Function
:函数类,每一个函数都是它的一个实例
- 关于元素对象和元素集合的
HTMLCollection
:元素集合类NodeList
:节点集合类HTMLDivElement
: html div标签类HTMLElement
: html标签类Element
: 标签类Node
: 节点类EventTarget
: 事件对象类
每一个HTML标签都有一个自己所属的内置类
//获取元素集合类
getElementsByClass
getElementsByTagName
querySelectorAll
复制代码
//获取节点集合类方法:
getElementsByName
childNodes
复制代码
目前阶段学习面向对象对于我们的意义
研究数组:
1、创建一个数组的实例,研究其基础语法和结构
2、如果想要研究数据具备的功能方法,我们只需要看Array/Object这些类上都赋予了它什么样的方法
问题
document.getElementById
它的上下文
只能是document
,为什么其他的不可以?(控制台dir(document))?
因为
getElementById
这个方法只有Document这个类才有
,其他标签类上没有;并且document
是Document
的实例,所以document
才能使用这个方法
我们遇到一个数据(对象)时 先考虑它是谁的实例,这个类提供给了它什么方法
基于面向对象创建数据值
//=>字面量创建方式
var arr=[12,23]
//严谨的基于面向对象(构造函数)方式创建一个数组
var ary=new Array();
复制代码
两种创建方式在核心意义上没有区别,都是创建Array这个类的一个实例,但是语法上是有区别的
- 1、字面量创建方式传递什么进来,都是给数组加入内容
- 2、构造函数创建方式
new Array(10)
;//创建一个长度为10的数组,数组中的每一项都是空new Array('10')
;如果只传递一个实参
并且实参不是数字
,相当于把当前值``作为
数组的第一项存储进来
new Array(10,20,30)
; //如果传递多个实参
,不是设置长度,而是把传递的内容当做数组中的每一项存储起来
var obj={name:'哈哈哈'}
var obj=new Object() //一般只用于创建空的对象,如果需要增加键值对,创建完成以后自己依次添加即可
复制代码
var num=12; //字面量创建出来的是一个基本数据类型值(但是也是Number的一个实例,可以调取Number赋予它的方法)
var num=new Number(); //构造函数方式创建出来的也是Number的一个实例(也可以调取Number赋予它的方法),但是获取的结果是对象数据类型的
复制代码
严谨的面向对象中,所有类创建实例都得通过new方式创建,创建出来的结果不可能出现基本数据类型值,都是对象数据类型值
但是基本数据类型值 都是对应类的一个实例 都能使用对应类的方法
构造函数设计模式(constructor)
在js当中,当我们使用
new XXX()
执行函数的时候,此时的函数就不是普通的函数了,而是变为一个类,返回的结果叫做当前类的实例(对象数据类型的this),我们把这种new XXX()执行的方式称之为构造函数设计模式
使用构造函数方式,主要是为了创建类和实例的,也就是基于面向对象编程思想来实现一些需求的处理
function Fn(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.getName=function(){
alert(this.name)
}
}
//Fn(); //普通函数执行
var f=new Fn(); //构造函数执行 Fn是一个类
//f就是当前类的一个实例 “构造函数设计模式”(我们一般都会把类名第一个字母大写)
/**
1、 没有显示的创建对象
2、 直接将属性和方法赋值给了this对象
3、 没有return语句
**/
function Fn2(){
console.log(1)
}
var f=new Fn2();
//Fn2的代码会先执行打印1,但是f是Fn2的实例是个对象,因为没有this.xxx=xxx的操作所以f是个空对象{}
复制代码
普通函数执行 VS 构造函数执行
普通函数执行
1、开辟一个新的 私有作用域
2、把创建函数的字符串 复制过来 变为真正的js表达式
3、形参赋值
4、变量提升
5、代码自上而下执行(return 后面的值就是当前函数返回的结果)
6、栈内存释放或者不释放问题
function fn(num){
this.num=num; //=>this指window 相当于给全局对象增加一个num的属性 属性值是10
var total=null;
total+=num;
return total;
}
var f=fn(10) //=>f:10
复制代码
构造函数执行
- 1、首先和普通函数执行一样,也需要开辟一个新的私有作用域
- 2、把创建函数的字符串 复制过来 变为真正的js表达式
- 3、在私有作用域中完成类似于普通函数的操作:形参赋值、变量提升
- 4、在代码自上而下执行之前 构造函数有属于自己的比较特殊的操作:
“浏览器” 会在当前作用域当中默认创建一个对象数据类型的值(开辟堆内存),并且会让当前函数中的 “执行主体(this)指向创建出来的对象”
- 5、像普通函数一样自上而下执行构造函数代码:
this.XXX=XXX这些操作都是给默认创建的这个对象增加属性名和属性值
- 6、代码执行完成以后,即使函数中没有return ,在构造函数模式中:
浏览器会默认的把创建的对象返回到函数的外面
(相当于 默认 return this;)
构造函数执行,既具备普通函数执行的一面,也具备自己独有的一些操作
在构造函数执行期间,浏览器默认创建的对象(也就是函数体中的this)就是当前这个类的一个实例,浏览器会把默认创建的实例返回,所以我们说:new Fn()执行,Fn是一个类,返回的结果是Fn这个类的一个实例
构造函数执行,构造函数体中的this是当前类的实例
(就是一个json)
实例是对象数据类型(json)
function Fn(num){
//=>在构造函数模式中,方法体中出现的this是当前类的一个实例(this.XXX=XXX都是给当前实例增加一些属性)
this.num=num;
}
var f= new Fn(10)
console.log(f.num) //10
复制代码
深入理解构造函数执行的步骤
当构造函数或者类,执行的时候不需要传递任何的实参时,此时我们是否加小括号就不重要了(不传递实参的情况下,小括号可以省略)
构造函数执行同时具备了普通函数执行的一面 也有自己特殊的一面,
但是和实例相关的只有自己特殊的一面
(也就是this.XXX=XXX才相当于给当前实例增加的属性)
函数体中出现的私有变量,和实例都没有直接的关系,只有this.XXX=XXX才和实例有关
在构造函数中,使用var,function声明的变量称为
私有变量
;私有变量的作用域,只在构造函数内有效
。即只能在构造函数内部使用,在构造函数外部,无论使用对象名还是类名都无法调用
通过类创建出来的每一个实例都是单独的个体(单独的堆内存空间),实例和实例之间是不相同并且独立互不影响的(
市面上部分开发把这种模式叫做单例模式,这种说法是错的,js当中这种模式叫做构造函数设计模式
)
在构造函数体中通过this.XXX=XXX给实例设置的属性都是当前实例的私有属性
构造函数体(默认返回的是实例:对象类型值)当我们自己手动设置了return时,
return的是基本类型值时
,对最后返回的实例没有任何影响
,但是如果返回的是引用数据类型的值时
,会把默认返回的实例替换掉不再是实例
function Fn(){
var num=100;
this.name='hahah'
this.sum=function(){}
}
var f1=new Fn(); //Fn 类;f1 Fn的一个实例
var f2=new Fn; //不传递实参的情况下,小括号可以省略
//私有变量和实例没关系
console.log(f1.num) //undefined
console.log(f1.name) //hahah
//不同的实例是不同的空间地址
console.log(f1===f2) //false
console.log(f1.sum===f2.sum) //false
复制代码
function Fn(){
this.name='aaa'
return 10;
}
var f=new Fn();
console.log(f) //f:{name:'aaa'} f依然是当前实例的一个实例
function Fn(){
this.name='aaa'
return {name:'bbb'};
}
var f=new Fn();
console.log(f) //f:{name:'bbb'} f不再是 Fn的实例,而是变为手动返回的对象了
复制代码
XX instanceof YY:测当前实例xx是否属于某个类yy
instanceof:用来检测当前实例xx是否属于某个类yy
instanceof解决了typeof无法识别是数组还是正则的问题
检测数据类型:typeof,instanceof,constructor,object.prototype.tostring.call
function Fn(){
}
var f=new Fn();
console.log(f instanceof Fn) //=>true
console.log([] instanceof Array) //=>true
console.log(/^$/ instanceof Fn) //=>false
console.log(f instanceof Fn) //=>true
[] instanceof Object
true
/^$/ instanceof Object
true
/^$/ instanceof Array
false
[] instanceof Array
true
/^$/ instanceof RegExp
true
复制代码
hasOwnProperty VS in
XX.hasOwnProperty(YY): 检测当前这个属性YY是不是XX这个对象的私有属性(不仅要是对象的属性,而且需要是私有的才可以)
YY in XX:用来检测当前这个属性YY是否隶属于对象XX(不管是对象的私有属性还是公有属性,只要有返回的就是 true)
var obj={
name:'aaa',
age:18
}
'name' in obj //true
'sex' in obj //false
'toString' in obj //true (公有属性)
'hasOwnProperty' in obj //true (公有属性)
//hasOwnProperty是Object这个内置类中提供的属性方法,只要当前对象是Object的一个实例,就可以使用这个方法
obj.hasOwnProperty('name') //true
obj.hasOwnProperty('toString') //false
复制代码
function Fn(){
this.num=100;
}
var f=new Fn();
f.hasOwnProperty('num') //true
复制代码
检测一个属性是否是当前对象的公有属性
1、是对象的一个属性
2、不是对象的私有属性
function hasPubProperty(attr,obj){
return (attr in obj) && (obj.hasOwnProperty(attr)===false)
}
hasPubProperty(hasOwnProperty,{XXX:'XXX'})
复制代码