什么是面向过程:
解决一个问题的时候会分析该怎么一步步的解决这个问题,遇到什么问题执行什么方法解决什么问题,最终达到向要的结果;注重的过程,在过程中一步步解决问题
什么是面向对象:
解决一个问题的时候会把事务抽象成一个个对象,分析每个对象拥有的属性和方法,最后让对象执行自己的方法进而解决问题;注重的是结果不在乎过程怎样,创建需要的对象,为对象添加各自的属性和方法
我们知道JS其实算是一门面向过程的编程语言,但是JS里为什么还有面向对象呢,因为在设计之初是参考Java的,所以就引入了对象这个概念,JS的对象都是基于这个全局的Object对象创造出来的,所以JS的面向对象更准确的说应该是基于对象
1 2 3 4 5 6 7 8 9 |
//(1)调用系统对象 var obj1=new Object(); obj1.name='heiehi'; obj1.age=16; //(2)字面量方法 var obj2={ name:'haha', age:17 } |
这两种基础方式创建的对象 ,只适合在创建单个对象,不量产的时候使用
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function Person(name, age) { var obj = new Object(); obj.name = name; obj.age= age; obj.myName = function() { console.log(this.name) }; return obj } var person1 = Person('lele', 20); var person2 = Person('wenli', 18); person1.myName(); person2.myName(); |
可以创建多个相似对象,可以量产对象;缺点:无法识别对现象的类型,返回的都是Object对象
1 2 3 4 5 |
function Person2(name,age) { this.name=name; this.age=age; } var obj3=new Person2('lala',18); |
构造函数解决了工厂模式无法识别对象类型的问题;缺点:每次实例一个对象的时候总是会重新创建构造器上的所有函数,这就造成了不必要的内存浪费
JS在使用new的时候内部所作的事情
1 2 3 4 5 6 7 8 9 10 11 12 |
function myNew(fun,...args){ //判断传值是否为函数 if(typeof fun !== 'function') throw new Error('fun参数必须为函数类型') //1.创建一个新的空对象 let obj = new Object(); //2.将新对象的原型指向构造函数的原型 obj.__proto__ = fun.prototype //3.执行构造函数,并将this指向新对象 let result = fun.apply(obj,args) //4.判断构造函数执行结果,如果是对象就返回执行结果,否则返回创建的新对象 return typeof result === 'object'? result:obj; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
function Person3() { } Person3.prototype.name = 'hahaha'; Person3.prototype.sayName = function() { console.log(this.name) }; var person33 = new Person3(); person33.sayName(); //更简单的方法,但是因为需要修改constructor指针并进制被枚举,所以还是能用上边那种最好 function Person4() { } Person4.prototype = { constructor:Person4,//以字面量方式创建对象,对象里的constructor就不再指向Person4,需要将constructor指针重定向为Person4 name:'我是谁', sayName: function() { console.log(this.name) } } var person44 = new Person4(); person44.sayName(); //一般情况下constructor是不可被枚举的所以应该设置一下 Object.defineProperty(Person4.prototype, 'constructor',{ enumerable: false, value: Person4 }) for(item in person44){ console.log(item);//没有constructor属性 } |
它解决了构造函数内存消耗的缺点;缺点:因为所有的属性和方法都是共享的,当一个实例改变了(不是重写)引用值类型(例如数组)的数据,其他实例上的这个属性都会跟着发生变化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//改变一个实例的数组其他实例数组跟着改变例子 function Person4() { } Person4.prototype = { constructor:Person4, name:'我是龙', children:['囚牛','睚眦','嘲风'], sayName: function() { console.log(this.name) } } var person44 = new Person4(); var person45 = new Person4(); person44.children.push('饕餮'); console.log(person44.children)//["囚牛", "睚眦", "嘲风", "饕餮"] console.log(person45.children)//["囚牛", "睚眦", "嘲风", "饕餮"] |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function Person5(name) { this.name = name; this.children = ['囚牛','睚眦','嘲风']; } Person5.prototype.sayName = function() { console.log(this.name) } var person55 = new Person5('lele') var person56 = new Person5('wenli') person55.children.push('饕餮') console.log(person55.children) //["囚牛", "睚眦", "嘲风", "饕餮"] console.log(person56.children) // ["囚牛", "睚眦", "嘲风"] console.log(person55.children === person56.children) //false |
组合模式结合了构造函数模式和模式的优点,是创建对象中最常用的方法;它可以让实例拥有自己的属性值副表,同时也可以使用原型上的共享方法;非要说有什么缺点的话,那就是构造函数和原型分开了,没有很好的形成一个整体
1 2 3 4 5 6 7 8 9 10 11 |
function Person6(name,age){ this.name = name; this.age = age; if(typeof sayName != 'function'){ Person6.prototype.sayName=function (){ console.log('我是动态原型创建的对象') } } } var person66 = new Person6('lele',20); person66.sayName(); |
动态原型创建对象就是为了解决组合式创建对象的缺陷,将所有的信息封装在构造函数中;这种方法就称的上是完美了;注意if判断一个在执行之后应该存在的方法或属性就好;不能用对象字面量修改原型对象
1 2 3 4 5 6 7 8 9 10 |
function Person7(name) { var obj = new Object(); obj.name = name; obj.sayName = function() { console.log('我是寄生构造函数模式创建的对象') }; return obj } var person77 = new Person7('寄生'); person77.sayName(); |
很少用到可做了解,基本上就是为扩展原生构造函数的,比如为Array扩展额外的方法
1 2 3 4 5 6 7 8 9 10 |
function Person8(name) { var obj = new Object() obj.sayName = function() { console.log('我是稳妥构造函数模式创建的对象') } return obj } var person88 = new Person8('lele'); person88.name // undefined person88.sayName() //lele |
基本上用不到,可做了解;最大的特点就是安全。。。。
我们知道面向对象的三大特性就是封装,继承,多态;封装不用多说了,就是能将够事物的属性和方法抽离出来封装在一个对象中,方便使用;多态就是同一个操作作用于不同的对象上,可以产生不同的解释和不同的执行结果 ,在js中对象的多态用到的不多;我们着重讲解继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function Animal(kind,sex){ this.kind = kind; this.sex = sex; } Animal.prototype.run = function (){ console.log('我是动物,我可以跑') } function Dog(color){ this.color = color; } Dog.prototype = new Animal('dog','雌性');//将Dog类的原型指向Animal的一个实例,这时候属性就已经固定了 Dog.prototype.play = function (){//Dog的私有方法必须写在new Animal之后要不然会被覆盖 console.log('我可以跟主人一起玩') } var Erha = new Dog('white'); Erha.run();//调用Animal上的方法 Erha.play();//调用自己类上的方法 |
1 2 3 4 5 6 7 8 9 10 11 12 |
function Animal(kind,sex){ this.kind = kind; this.sex = sex; } Animal.prototype.run = function (){ console.log('我是动物,我可以跑') } function Cat(){ Animal.apply(this,['猫','雌性'])//使用apply将Cat指向Animal } var cat1 = new Cat(); console.log(cat1); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Animal(kind,sex){ this.kind = kind; this.sex = sex; } Animal.prototype.run = function (){ console.log('我是动物,我可以跑') } function Pig(kind,sex){ Animal.apply(this,[kind,sex]);//借用构造函数继承属性 } Pig.prototype = new Animal();//原型链继承方法 Pig.prototype.constructor = Pig;//需要将constructor指回Pig var pig1 = new Pig('猪','公'); console.log(pig1.kind,pig1.sex,Pig.prototype.constructor); pig1.run() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
var bird = {//字面量对象 kind:'鸟', fly:function (){ console.log('我是鸟,我可以飞') } } function Obj(o){//原型式继承定义的函数;将一个prototype指向选哟继承的对象的空对象返回出去 function F(){}; F.prototype = o; return new F(); } var peacock = Obj(bird); console.log(peacock.kind); peacock.fly();//继承了bird上的飞翔方法 //es5中新增了Object.create()方法(IE9+和现代浏览器);规范了原型式继承,可以用Object.creat()方法写原型式继承;有两个参数第一个参数是要继承的对象,第二个是当前对象上的属性集合对象,属性值必须写在一个对象里 var sparrow = Object.create(bird,{ name: { value: '我是麻雀' }, eat: { value:function (){ console.log('我吃五谷') } } }); console.log(sparrow.kind,sparrow.name); sparrow.eat();//自己的方法 sparrow.fly();//继承bird的方法 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var bird = {//字面量对象 kind:'鸟', fly:function (){ console.log('我是鸟,我可以飞') } } function creatBird(obj){//将原型式继承用函数包括起来,使之看起来更像继承 var magpie = Object.create(obj); magpie.sayHi = function (){ console.log('我是喜鹊,我会报喜') } return magpie } var magpie1 = creatBird(bird); magpie1.fly();//继承bird上的方法 magpie1.sayHi();//自己的方法 |
将原型式继承和自己的方法写在一个函数中,使之成为一个整体看起来更像是一个继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function Animal(kind,sex){//超类 this.kind = kind; this.sex = sex; } Animal.prototype.run = function (){//超类方法 console.log('我是动物,我可以跑') } function CreatInherit(sub,sup){//寄生式继承,继承超类的原型上的属性 var clone = Object.create(sup.prototype);//复制一个父类原型副本 clone.constructor = sub;//将指针指向子类对象 sub.prototype = clone;//将子类对象原型指向复制的父类对象 } function Eagle(kind,sex){//借用构造函数继承超类上的属性 Animal.apply(this,[kind,sex]); } CreatInherit(Eagle,Animal); Eagle.prototype.sleep = function (){//实例原型的属性需要写在实力原型改变指向的后面,要不然会被覆盖 console.log('我可以在天上睡觉') } var Eagle1 = new Eagle('老鹰','雄性'); Eagle1.sleep();//实例原型上的方法 console.log(Eagle1.kind);//继承Animal上的属性 Eagle1.run();//继承Animal的run方法 |
组合寄生式继承结合了借用构造函数式继承的优点和寄生式继承的优点,完美的解决了伪经典模式运行两次构造函数的缺点,我们称之为完美继承;如果非要说有什么缺点,那就是写起来比较麻烦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Plant{//定义植物类 constructor(kind,age){//constructor里写属性 this.kind = kind; this.age = age; }; grow(){//原型上的方法,直接写就行 console.log('我是植物,我可以生长') }; static flowering(){//静态方法可以在类上使用,实例不能继承,不能在实例上使用 console.log('我可以开花') } } class Poplar extends Plant{//子类继承父类属性和方法 constructor(kind,age,color){ super(kind,age);//super()代表父类构造函数;super作为对象时,在普通方法中指向父类的原型对象,在静态方法中指向父类;必须执行一次super() this.color = color; }; hardGrowing(){ console.log('我是白杨,努力生长') }; } let poplar = new Poplar('白杨',500,'白色'); console.log(poplar.kind,poplar.age,poplar.color);//继承父类的属性和自己的属性 poplar.hardGrowing()//子类自己的方法 poplar.grow();//继承父类的方法 Poplar.flowering();//子类可以继承父类的静态方法,但是不能被实例继承 |
ES6继承在语言层面上提供了面向对象的部分支持,在模式上使用的是组合寄生式继承,用起来更直观更方便更标准化,也支持原生对象的继承;但是也有一些缺点,比如不能设置静态属性(可以设置静态方法);class中不能定义私有属性和方法,所有的属性和方法都会被子类继承,可以写在class外,但是又破坏了对象的整体性
在ES5中没有类的概念,我们用function来模拟类,类就是保存了一个函数的变量,这个函数又自己的属性和方法;把客观事物封装成抽象的类,隐藏属性和方法的实现细节,仅对外公开接口就是封装;个人理解,创建对象的过程就是在封装对象
所谓多态,就是指一个引用类型在不同情况下可以有多种状态。多态在js中用的比较少
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function Nature(kind){ kind.sayName(); } function AnimalAll(name){this.name = name} AnimalAll.prototype.sayName = function(){ console.log('我是'+this.name) } function PlantAll(name){this.name = name} PlantAll.prototype.sayName = function(){ console.log('我是'+this.name) } Nature(new AnimalAll('动物'));//我是动物 Nature(new PlantAll('植物'));//我是植物 |