JS面向对象的编程

函数功能:把所有的功能都的封装在一个函数里,等下次需要实现这个功能时,运行这个方法即可。

面向对象:object oriented programming 以对象数据类型为导向的编程

单例模式

概念:将同一事物的属性和方法封装在一个空间(对象)里

防止代码冲突和覆盖

不同模块之间获取属性:通过模块名.方法名()

同一模块之间获取属性:this.方法名()

//单例模式是敞开型的
var login = {
     username: 'lucy',
     password: '123456',
     email: function(){
            console.log('email')},
     fn:function(){console.log(1)},
     fn1:function(){
              this.fn();//调用同一模块方法
              console.log(2)}
};
login.fn1(); //调用fn1方法
var register = {
    username: 'lucy',
    password: '123456',
    fn2:function(){
             this.username;
             this.password;},
    fn1:function(){
             login.email()//不同模块调用
              }
}

高级单例模式:

单例模式里的任何内容都能被提取;但有些内容是模块私有的,只有这个模块内部才能调用,若要公有,得经过处理,把需要暴露的东西放在return后的对象中返回。

解决私有和共享问题

var login = (function(){
     var username = 'Lily';
     var password = '123456';
     function email(){
           console.log('email');
         }
     function check(){
           console.log('check');  
         }
     return {
            email: email(),
         }
})()//小括号必须写 是把自执行函数返回的结果给了login 而不是把函数给了login
var register = {
    fn: function(){
        login.email; //调用login里的email方法
        console.log('email');    
    }
}
register.fn();

工厂模式:

单例模式属于手工作坊式(一遍一遍执行),''批量生产''需要使用工厂模式

共三步  生产出来的是一模一样的东西

function shirt(){
        //1:创建对象
        var obj ={};
        //2:给对象添加属性(把需要实现的功能放在对象里)
        obj.size = 'xl',
        obj.material = '棉',
        obj.fn = function{
             console.log('fn');    
        }
        //3:返回对象
        return obj;
}
for(var i = 0; i < 1000; i++){
    console.log(shirt());//运行1000次
}

构造函数模式: 

对象:万物皆对象

类:对象中细分的类型

实例:某类中实实在在的事物(具体的事物)

ES5中类叫构造函数 ES6中 class 才叫类

JS中的内置类:

Number String Boolean Null Undefined Object Array RegExp Date Functon 小写叫数据类型

Number String Boolean 包装类

var num = 10;//可以操作的具体的事物  实例 属于Number类
   var str = 'abc';//String类的实例

   var num = new Number(10); //Number{10}  对象10
   num = num.valueOf(); //10 基本数据类型

   var ary = new Array(10);//参数是数值 表示数组长度
   var ary = new Array('abc');//表示数组中有一项是abc
   var ary = new Array(10, 20);//超过两项 表示数组有两项10,20

自定义类

为了和普通函数区别:首字母要大写

function Person(){  //为了和普通函数区别:首字母要大写
        //1.创建对象(系统自己创建)
        //2.给对象添加属性(对象用this来表示)
        this.name = 'Lily';  //this称为实例
        this.age = 20;
        this.writeCss = function () {
            console.log('writeCss');
        }
        //3.返回对象(系统自己返回)
    }
    //Person();//这样运行--->Person就是普通函数,this指的是window
    var p1 = new Person();   //通过new运算符运行的函数称为构造函数 称为Person构造函数(Person类)  p1指的是返回的对象 也就是Person类的实例
    var p2 = new Person();
    console.log(p1, p2);

函数细节知识:

1.若不需要传参,小括号可以省略

2.this指的是实例,跟this.xxx有关,跟私有变量无关

3.只要在构造函数中定义的属性,都是实例私有的属性(有实例都要用的功能公有的功能不能把他放在构造函数里,放在原型上)

4.return不用写。若必须写,return后面跟基本类型数据,对return没有影响,return后面跟引用类型的数据会把实例覆盖

``` function Person(name, age){ var name = 'Lily'; //私有变量name this.name = name; this.age = age; this.fn = function(){ console.log('fn'); } } var p1 = new Person('Lucy', 22); console.log(p1.name); //跟私有变量无关 ``` ``` function Person(name, age){ this.name = name; this.age = age; this.fn = function(){ console.log('fn'); } } var p1 = new Person('Lucy', 22); var p2 = new Person('Lily',10); console.log(p1.fn == p2.fn);//false ``` ``` function Person(name, age) { this.name = name; this.age = age; } Person.prototype.fn = function (){ console.log('fn'); };//每个实例都共有的 放在原型上 var p1 = new Person('Lucy', 22); var p2 = new Person('Lily',10); console.log(p1.fn == p2.fn);//true ``` ### 原型模式 记住三句话: - 1.所有的函数都有一个属性叫`prototype`属性,prototype指向默认的原型对象 - 2.默认的原型对象上有个属性叫`constructor`,它指向构造函数 - 3.所有的对象上都有一个属性`__proto__`,它指向所属类的原型(IE不支持) ![Alt text](./1536641006111.png) `Object.prototype`没有下划线`__proto__`(原本应该有但是没有,为null) ![Alt text](./1536641183313.png) ``` function Person(name, age) { this.name = name; this.age = age; } Person.prototype.fn = function (){ console.log('fn'); };//每个实例都共有的 放在原型上 var p1 = new Person('Lucy', 22); var p2 = new Person('Lily',10); console.log(p1.fn === p2.fn);//true console.log(Person.prototype.__proto__ === Object.prototype);//true console.log(p1.__proto__ === Person.prototype);//true console.log(p1.__proto__.__proto__ === Object.prototype);//true ``` ![Alt text](./1536650289036.png) - `hasOwnProperty`:检测是否是一个私有属性 - `isPrototypeOf`检测一个对象是否在另一个对象的原型链上 - `propertyIsEnumerable`:属性是否是可枚举的 - `toString` 转换成字符串 思考题:封装一个方法hasPublicProperty:检测一个属性是否是共有的 ``` function hasPublicProperty(obj, attr) { if(attr in obj) { if (obj.hasOwnProperty(attr)) console.log('私有属性'); else console.log('共有属性'); }else{ console.log('不是它的属性'); } } function Fn(){ this.name = 'Lily'; } Fn.prototype.fn = function () { console.log('a') }; Fn.prototype.age = 26; var f1 = new Fn(); hasPublicProperty(f1, 'name');//私有属性 hasPublicProperty(f1, 'fn'); //共有属性 hasPublicProperty(f1, 'age'); //共有属性 console.log('hasOwnProperty' in f1); //true //属性是否在f1对象的原型链上 ``` - 用`in`来判断属性是否在原型链上 - 查找属性的顺序: - 1.若实例上有,则不会继续往上查找 - 2.实例上没有,则通过`__proto__`去所属类的原型上找,若还没有找到,则继续通过`__proto__`查找,一直找到`Object类`的原型,若还是没有找到,则是`undefined` > 注意:通过`__proto__`查找所属类的原型,一级级往父类的原型找,就形成了原型链 `__proto__`是实现`继承`的关键 #### 原型上扩展方法 - 内置类原型上的方法可以一个一个修改,但不能批量修改 - `__proto__` IE浏览器不支持 ``` var ary = [1, 2]; console.log(ary.toString());//'1, 2' Array.prototype.toString = function(){ return this.join('+'); //this指的是实例 } console.log(ary.toString());//'1+2'//重写内置类里的方法 random(4)//随机删除数组四项 ``` #### 原型上 链式调用 `return this`实现链式调用的关键 ### 深入函数 - 普通函数 - 构造函数 - 对象 - 增删改查 > 若函数当成对象时,函数名就相当于对象名 ``` function fn(){ console.log(1); } fn.aa = 33;//这种属性称为静态属性。没有动态属性!! console.log(fn.aa); ``` > 当成函数时,有一个属性`prototype`,指向原型对象 > 当成对象时,有一个属性`__proto__`,指向所属类(Function类)原型 ``` var fn = new Function('a','b','return a+b'); ``` ![Alt text](./1536718306231.png) --->由此证明:所有的函数都是`Function类`的`实例` ``` Function.__proto__ == Function.prototype //true fn.__proto__ == Function.prototype //true ``` ![Alt text](./1536719640822.png) ### 面试题 ``` //instanceof 后一个对象是否在前一个对象的原型链上 Function instanceof Object //true Function.prototype.__proto__ == Object.prototype; //true Function.__proto__.__proto__ == Object.prototype; //true Object instanceof Function //true Object.__proto__ == Function.prototype //true ``` ![Alt text](./1536720497259.png) ![Alt text](./1536723353685.png) ### 阿里面试: ``` function Foo(){ //构造函数 getName = function () { //不是参数 没有var 找上一级的 console.log(1); }; return this; //当普通函数运行时,前面没有.,this指的是window } Foo.getName = function () { console.log(2); }; Foo.prototype.getName = function () { //共有属性getName console.log(3); }; var getName = function () { //匿名函数 console.log(4); }; function getName(){ //普通函数 console.log(5); } Foo.getName(); //2 普通函数执行 getName(); //5 4 Foo().getName(); //1 getName();//1 new Foo.getName();//3 2 function() {console.log('2')}把匿名函数当成构造函数运行 new Foo().getName();//3 创建一个实例 找实例里有没有 ``` ### Function类 ![Alt text](./1536723464721.png) #### `call``apply``bind` - 作用:用来改变方法里的.之前的this关键字 ##### `call` - 第一个参数用来改变点前面方法里的`this`关键字,第二个参数开始会当成实参传给.前面的方法(传参方式:`散列式`) - 调用方法: - 1.Function.prototype.call; - 2.fn.call; ``` function fn(a, b) { console.log(this); } console.log(fn.call); //实例去调用原型上的方法 ƒ call() { [native code] } fn.call({});//将fn中的this改成{} fn.call(10);//将this改成{10} fn.call(10,20,30);//将this改成{10},形参a=20 b=30 ``` > 说明: Function.prototype.call = function(){ //1.把实例(点前面的方法)中的this改成第一个参数 //2.把实例运行 } ##### `apply`- 参数:两个参数。第一个参数用来改变.前面方法里的`this`,第二个`数组`或者`类数组`然后一起传给.前面的方法(传参方式:`打包式的`)- 传参时是打包式的,但是实参传给形参时,还得把实参里的内容拿出来,一个一个传给形参(第一项给第一个形参,第二项给第二个形参)``` function fn(a,b){ console.log(this+a+b); } fn.apply({},[20,30]);//[object Object]2030```##### `bind`> 柯里化函数(预处理思想)- 第一个参数:改变.前面的`this`关键字(传参方式:同`call`,都是散列式)- 把所有的参数返回给.前面的方法后,返回一个需要执行的小函数,想要执行时要自己手动执行``` function fn(a, b, c){ return(this, a, b, c); } var res = fn.bind({}, 10, 20); res(30); res(50); Function.prototype.bind = function(context){ //把第二个参数开始多有实参拿到 //this实例保存下来 return function(){ //把小函数的所有的参数跟上面参数合并 //实例.apply(context,所有的参数) } }```

猜你喜欢

转载自www.cnblogs.com/Lia-633/p/9786537.html