面向对象的编程思想------抽象过程----------实例化的过程

面向对象的编程思想------抽象过程----------实例化的过程
1)根据需求,抽象出相关的对象
2)总结对象的特征和行为,把特征变成属性,行为变成方法
3)定义构造函数,实例化对象
4)通过对象调用属性和方法,完成相应的需求


面向过程:凡事都要亲力亲为,每件事的过程都知道,注重的是过程
面向对象:根据需求找对象(封装好的函数)所有的事情都用对象来做,注重的是结果,唯一的过程是调用
面向对象特性:封装,继承,多态
JS不是面向对象的语言,但是可以模拟面向对象的思想
JS是一门基于对象的语言,JS有很多内置对象,JS可模拟面向对象,JS也可以创建对象
JS没有继承和多态,但是可以模拟出来
封装指的是把代码放到一个地方
什么是对象,特指某个东西,有属性和方法具体特指的代码集合体
在当前对象的方法中可以访问当前这个对象的属性的值,可以使用this值,使用当前值
this总是指向调用当前方法或属性的对象
如果是一个对象当作参数传入进了一个函数进行操作,那么不需要return语句也能访问操作过的对象
1:创建对象的在内存空间里面过程:
1)无论是通过何种形式创建的对象,在内存中的形式是一样的,对象赋值给变量
2)对象实例放在内存堆中,有一个地址,而变量存储在内存的栈里面,而栈里面
就存储着对象实例在”堆“里面的地址
1:创建对象的三种方式
1:调用系统的构造函数创建对象
object类型是系统自带的,类似于Array,直接调用用于创建对象
var obj=new object(); //变量存储对象,也叫实例化对象,obj就是对象类型的“实例”,说白了就是个特指的例子
添加属性 obj.name=值
添加方法 obj.name=函数
instanceOf 用于判断变量是否属于某个类型
如何一次性创建多个对象
把创建对象的代码封装在一个函数中
function createObject(属性值1,属性值2,){
var obj=new Object();
obj.name="属性值1"
obj,age=“属性2”
return obj
}
2:自定义构造函数创建对象(结合第一种结合工厂模式创建)
function Person(属性值1,属性值2,){
this.name="属性值1";
this.age=“属性值2”;
}
var xiaoMing=new Person("明",22);
说明:
1)函数和构造函数的区别:首字母是否大写
2)构造函数是为了创建对象,但是也可以当作普通函数来用
3)构造函数取代了createPerson函数,没有显式地创建对象,直接将属性和方法赋值给this对象,没有return语句
优点:
相对于工厂模式,自定义构造函数意味着将来可以将它的实例标识为一种特定的类型,说白了,就是方便区分
也为了多样化地创建对象,比如,我希望创建人类型的对象,我们就可以构造一个Person(人)构造函数,里面
定义好的属性和方法和人是契合的,比如我想创建动物类型的对象,我们就可以构造一个animal(动物)构造函数
里面的的属性和方法都是和动物契合的,
注意:当然,任何函数都可以当作构造函数,只需要在调用的加new就行了
创建过程:
1)在内存中开辟(申请一块空闲的)空间,存储新创建的对象
2)把this设置为当前的对象(实例对象)
3)设置对象的属性和方法
4)把this这个对象返回
5)最后存入变量

默认使用大写字母开头
3:字面量的方式创建对象
举例:
var num=10; //字面量形式赋值
var arr=[ ]; //字面量形式创建数组(空)
var obj={ }; //字面量形式创建对象(空)
语法:
var obj={
name:"小明",
age:20,
eat:function( ){.....};
}
Ps:中间用逗号隔开
4:通过原型的引入解决多个实例化对象里重复创建相同功能方法的问题
比如实例化一万个Person对象,就会创建一万个功能相同,但是不相等的方法,浪费内存,但是将方法定义在
外面,比如这样
function eat(){ console.log("xx")};
function Person(name,age){
this.eat=eat;
}
容易被同名变量替换掉,所以,我们可以引用原型来解决这个问题:
Person.prototype.eat=function( ) { console.log("xx")};
function Person(name,age){
this.eat=eat;
}
这样就完美了,实例对象里面并没有这个方法,只会通过实例对象的__proto__属性指向原型对象prototype里
的eat()方法,所以就可以使用
4:另一种访问对象属性的方法
语法:
obj["属性名"]
obj["方法名"]( );
5:总结
字面量创建对象的缺陷:无法传值,字面量创建对象的方式就是改进版的构造函数创建对象方式
构造函数创建对象的缺陷:
同类型实例对象调用方法时obj1.eat不等于obj2.eat//eat是自定义构造函数的方法
每个不同实例对象里面的方法都不相等,当有多个实例对象时,是比较浪费内存的

6:构造函数和实例对象和原型对象之间的关系
1)构造函数用来实例化对象,构造函数里面本身不包括属性和方法,属性都在实例化对象里面
2)构造函数里面有一个原型对象prototype,里面存储着方法,还有一个构造器属性:constructor
这个东西才是用来创建实例对象的关键,它指向构造函数本身;
3)实例化对象里面存储着自己的属性,但是并没有存储着方法,但是每个实例对象里有一个__proto__属性,
它指向构造函数里的prototype对象,而里面存储着方法,每个实例化对象就是这样调用方法的,实例对象
可以直接访问原型里面的方法
7:利用原型共享数据
1)除了方法,属性也可以写在原型中
语法:Object.prototype.共享属性=值
2)原型也是对象,所以可以替换写法为字面量写法:
Object.prototype={
constructor:Object, //手动指向
共享属性1:值1,
共享属性2:值2,
方法1:function(){.......}
};
注意:这种方法必须手动将constructor指向构造函数,不然原型里面是没有的
8:实例对象的方法可以相互调用
语法:
this.play=function(){ this.eat() }
this.eat=fucntiong(){。。。。。。。。。}
同样,原型当中的方法也可以相互访问
语法:
Object.prototype.eat=functiong(){ this.play()}
Object.prototype.play=function(){。。。。。}
9:实例对象使用同名属性和方法的顺序
举例:
function Person(sex){
this.sex=sex;
}
Person.prototype.name="女";
var per=new Person("男");
console.log(per.sex) //男
说明:
当调用这个属性时,系统会先从实例对象里面找,如果没有,再到原型对象里面找,这叫做原型链,方法也是一样
10:为内置对象添加原型方法
Array() String() Date()这些都是系统自带的构造函数,当我们实例化它们并调用方法的时候,发现实例对象
调用的方法基本都放在这些构造函数的原型对象(prototype)中
1)那我们是否可以为系统的对象的原型添加方法呢(改变源码)
比如,我想字符串有一个倒序字符串的方法
String.prototype.mymath=function(){ .... }
这是可以的
11:原型对象中添加属性
语法:
function Person(){
this.a=0;
}
Person.prototypr.aaa=function(){
this.a=100;
}
var per=new Person
Person.aaa()
console.log(per.a) //100
说明:后面声明的属性要调用后才能生效
12:bind方法改变function的指向
语法:Game.prototype.automaticRun=function(){
var that=this;
this.snake.init();
setInterval(function(){
//这里使用that指向Game对象,this则是指向window对象的,定时器是属于window的对象
this.snake.move();
this.snake.init();
}.bind(that),150);//这里的bind方法可以改变this的指向
};
13:属性方法调用的另一种写法
var per=new Person()
per["属性名"];
per["方法名"]();
14:原型的指向可以改变
实例对象的原型(__proto__)指向构造函数的原型对象(prototype),但是构造函数的原型对象是可以改变的,因为构造函数
本身也是一个对象,可以写成字面量的形式,所以可以将原型对象指向另一个构造函数,而实例对象原本的指向就失效了
语法:
//定义一个构造函数
function Person(age,name){
this.active="复读机",
this.age=age,
this.name=name
};
//Person的原型对象
Person.prototype={
type:"人类",
eat:function(){
console.log("我要吃");
},
constructor:Person
}
//定义另一个构造函数
function Student(){

};
//将这个构造函数的原型对象指向另一个构造函数的实例对象
Student.prototype=new Person(12,"渣渣");
//实例化这个构造函数
var str=new Student();
console.log(str.name);//渣渣
理解:这种将一个构造函数的原型对象指向另一个构造函数实例的方式叫做原型链,我们可以想象一下,我们定义了一个人类对象,
里面定义了一些基本的人的属性和方法,然后我们定义了一个学生的构造函数,因为他们本质上也属于人类,所以我们将原型
对象指向它,这样学生的实例对象就可以访问到人的一些属性和方法
说明:__proto__指向构造函数的prototype,prototype对象里面也有__proto__,而__proto__指向了某个构造函数的原型prototype
以此类推
15:Object的原型对象prototype的__proto__指向的是null
理解:构造函数有一个原型对象prototype,而原型对象也是一个对象,有一个自己的原型__proto__,指向Object对象,
而Object的原型对象就是Object本身,而object的原型对象prototype的原型__proto__是null;
总结:任何对象都是Object的实例,而Object就是原型链的终点


16:原型指向改变了还怎么正常添加自己的原型方法呢~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我们只需要在改变指向后再添加原型方法就可以了
语法:
Student.prototype=new Person(12,"渣渣");
Student.prototype.sayHi=function(){
console.log("靓仔,你好呀");
}
说明:这种方式其实并没有将指向改回来,或者同时指向两个方向,看似将方法定义在了自己的原型对象中,
实则是将方法定义在了另一个构造函数的实例对象中
Student.prototype.sayHi相当于
new Person(12,"渣渣").sayHi=function(){......}
PS:抽象写法
17:封装
封装:就是包装
比如:一个值存储在一个变量中
一些重复代码放在一个函数中
一系列的属性和方法放在一个对象中
一些对象放在一个js文件中
18:多态
一个对象有不同的行为,或者是同一个行为针对不同的对象,产生不同的结果,想要有多态,就得先有继承
19:类
面向对象的编程语言中有类(class)的概念,(一种特殊的数据类型)
20:继承
继承是一种关系,类于类的关系,由于JS没有类,但是可以通过构造函数来模拟类,然后通过原型来实现继承
继承是为了数据共享,JS中的继承也是为了实现数据共享
原型的作用1:数据共享,节省内存空间
原型的作用2:为了实现继承
继承是一种关系:
父类级别与子类级别的关系
举例:父级类别有的子集类别肯定有,子集类别有的父级不一定有
21:原型链继承的问题
语法:
Son.prototype=new Father(agu1,agu2);
我们通过实例父级构造函数来给子级构造函数原型赋值,这样导致属性确实继承了,但是共用属性的值全是一样的
22:借用构造函数继承
我们可以通过call方法来改变子级构造函数的this指向
语法:
function Son(agu1,agu2,agu3){
Father.call(this,agu2,agu3)
this.scroe=scroe;
}
解析:把Person的this指向指向了Son的this指向,也就是son的实例对象,所以person实例化就相当于Son实例化
构造函数.call(当前对象,属性,属性,属性)
//解决了属性继承,并且值不重复
//缺陷:父级类别中的方法不能继承
23:call方法详解
一句话,改变对象this的指向
通俗一点:我想要使用其他对象的某个方法
语法:
其他对象.方法名字.apply(当前对象,参数,.......);
语法
比如function a(){.....} b={}
a.call(b)
得到
b={
function a(){.....}
}
解析:把a的this(window)指向b,相当于把函数a放入b对象中,这样函数a就指向了对象b
需要注意的是函数a也是对象,a是对Function对象的引用,把add放入obj对象下也就是让add成为obj对象的属性或者方法
但是又不会真正的存在 于obj对象中,用过就删除了
一、如果apply和call不传入参数或者传入null,那么调用该方法的函数this就指向默认的window
二、apply和call方法传入的第二个及后面的参数都是调用该方法对象的参数,如a.call(obj,arg1,arg2),其中arg1和arg2就是a对象的
参数,并且这两个方法可以用作调用函数,如果函数有返回值,调用方法也可以通过赋值的形式输入返回值
三、apply和call唯一的区别就是传入参数的形式,apply使用数组形式传入参数,call使用字面量形式
四、apply和call改变函数或对象this的指向,指向第一个对象参数,但是并不真正属于指向的对象中,
五、这两个方法存在于Function构造函数的原型prototype中
24:组合继承
原型继承+借用构造函数继承
语法:
function Son(agu1,agu2,agu3){
Father.call(this,agu2,agu3)
this.scroe=scroe;
}
Son.prototype=new Father();
解析:调用call()实现的借用构造函数继承无法继承父级类别的原型方法,所以我们可以组合原型链继承的方法,特点是实例化时不
需要传入参数,只为了使用原型方法(因为已经通过call继承了父级类属性)
25:拷贝继承
把一个对象当中的属性和方法通过循环遍历放到另一个对象当中
function Person(){

}
Person.prototype.name="小米";
Person.prototype.age=33;
Person.prototype.sex="女";
Person.prototype.sayHi=function(){
console.log("啊哈哈")
}
//定义空对象
var jse={};
for(var key in Person.prototype){
//forin循环给jse动态设定key属性并赋值
jse[key]=Person.prototype[key];
}
26:总结继承
原型的作用:数据共享,节省空间
继承的作用:数据共享,节省空间
原型链继承:改变原型的指向,共享属性值不能改变
借用构造函数继承:主要解决属性值不能改变的问题,但是无法无法继承原型对象
组合继承:原型链继承+借用构造函数继承,解决属性值以及无法继承原型对象的问题
拷贝继承:就是把对象中的属性和方法,直接以遍历的方法复制到另一个对象中
27:同名原型属性和共有属性的问题
一句话:如果内部属性没有赋值,那么相同名的原型属性将覆盖内部属性,如果内部属性赋值了,那么相同名的原型属性将不起作用!
28:函数进阶
函数的声明
function f1(){}
f1()
函数表达式
var f2=function(){}
f2()
注意:函数的声明如果放在if-else的语句中,在IE8的浏览器中会出现问题,IE8会把else里面的声明也预先解析 。。
但是可以通过函数表达式的方式来解决这个问题,先定义函数,再放入if-else语句中进行判断赋值,在IE8中就不会出现问题
29:bind()方法以及和另两种方法的案例区别
一、bind方法功能类似于apply(),call(),用于改变对象的this指向但是返回的是一个函数,如果调用需要再加一个括号,
像这样:obj.bind()()
二、语法:obj.bind(targetObj,arg1,arg2),第一个参数在传入null的时候this默认指向window
三、bind()方法和call(),apply()最大的区别是bind()方法只会复制函数并不会执行,call和apply是直接执行函数,典型的案例就是
在设定定时器中function的指向是,使用bind可以正常使用定时器,而用call和apply虽然也可以改变this指向,但是只会输出一次
30:函数中的一些属性
name:函数的名字,只读,不能修改
arguments:实参的个数
length:形参的个数
caller:调用该函数的函数
31:高阶函数之函数作为参数
一、当函数作为参数时,传入后调用将会正常执行,不能加括号,当加括号时,提供的就是该函数的返回值(前提参数函数有返回值)
二、

32:作用域、作用域链、预解析
变量:局部变量,全局变量
作用域:变量可以使用的区域
块级作用域:变量可以使用的区域,指一对大括号{}里面的东西,JS没有块级作用域
作用域链:内部作用域可以使用外部的数据,层层查找
预解析:变量的声明、函数声明(包括里面的代码)会提升到当前作用域最上面,变量的赋值,函数表达式的赋值位置不变
33:闭包
有函数闭包,和对象闭包,差不多一个意思
解析:主要是依据作用域链来完成的,在某一非全局作用域定义的局部变量用来缓存数据
语法:1函数内部声明局部变量或者代码------>局部变量,缓存数据
2函数内部返回的函数里面使用变量,并在上一级作用域调用该函数
3每次使用返回值(函数)就会初始化一次变量,变量就延长释放时间-------->占用内存,缓存数据
优点:缓存数据,缺点:缓存数据,浪费空间
34:沙箱,沙盘
虚拟的环境,在里面运行代码,不会对外面产生影响(避免重名冲突等),相当于一个测试用的虚拟环境
实现方法:使用局部变量,函数的自调用
语法:
var num
(function(){
var num
等一系列封闭环境代码,和外界不冲突
}())
解析:我们可以把自调用函数里看作一个封闭的环境,我们在里面定义并运行的代码由于作用域的关系不会和外界冲突
平时写代码可以把不想被影响(比如重名)的代码块都放到沙箱里面,如果想要传出变量,需要手动设置为window的属性
35: 递归
函数中调用函数自己,一定要有结束条件
语法:function f1(){
执行代码
f1()
结束条件,避免无限调用
}
f1()
注意:递归不要轻易使用,效率低下
36:浅拷贝
把一个对象中的属性整个复制给另一个对象
深拷贝:不仅复制属性方法,而且还复制新的对象
37:数组和伪数组
数组是Array构造函数的实例对象,伪数组是Object构造的实例对象,通过属性名来模拟数组的下标,达到lei'si
1:数组有length属性,伪数组没有
2:数组长度可变,伪数组长度不可变(指length动态获取长度)
3:数组可以使用Array的原型方法,伪数组不可用Array的原型方法

猜你喜欢

转载自www.cnblogs.com/loveHtml/p/12102560.html