JavaScript笔记3--封装JS对象

1. js原始类型

js有5种原始数据类型:即 Undefined、Null、Boolean、Number 和 String。

2. js对象分类


js对象可以分为3类:本地对象、内置对象、宿主对象。

本地对象:ECMA-262 把本地对象定义为“独立于宿主环境的,由ECMAScript 提供的对象”。
再来看一下,“本地对象”包含哪些内容:
Object
Function
Array
String
Boolean
Number
Date
RegExp
Error
EvalError
RangeError
ReferenceError
SyntaxError
TypeError
URIError
本地对象就是 ECMA-262 定义的类(引用类型)。

内置对象:ECMA-262 把内置对象定义为“独立于宿主环境的,由ECMAScript 提供的对象,在 ECMAScript 程序开始执行时出现”。
这意味着开发者不必明确实例化内置对象,它已被实例化了。直接使用就行了。
ECMA-262 只定义了两个内置对象,即 Global 和 Math (它们也是本地对象,根据定义,每个内置对象都是本地对象)。

Global对象的属性列表:
这里写图片描述

Global对象的方法:
例如isNum(),isFinite(),parseInt(),parseFloat()等,都是Global对象的方法。
encodeURI():对传递给浏览器的URI进行编码。对应的解码函数:decodeURI()。
encodeURIComponent():对传递给浏览器的URI进行编码。对应的解码函数:decodeURIComponent()。
这两个方法的主要区别是:
encodeURI()不对URI中的特殊字符编码,例如冒号、斜杠、问号和英镑符号;
encodeURIComponent()会对它发现的所有非标准字符进行编码。而对应有decodeURI()和decodeURIComponent()解码函数。
这些URI方法代替了BOM中的escape()和()方法。URI方法更可取,因为它们会对所有的Unicode符号编码,而BOM方法只能对ASCII符号正确编码。应尽量避免使用escape()方法。

Math对象常用方法:
min()、max()
abs()方法返回数字的绝对值。
ceil()表示向上舍入
floor()表示向下舍入
round()标准的四舍五入

宿主对象:由 ECMAScript 实现的宿主环境提供的对象。所有 BOM 和 DOM 对象都是宿主对象。
BOM对象:window、location、screen、history、navigator。
DOM对象:document、form、button等。

开发人员可以自定义对象。

3.封装JS对象

JavaScript里一切皆对象。所以函数也是对象,是Function的实例。函数名可以理解为指向该实例的引用。
JS里封装对象的常用方式有5种:
显示自定义、工厂方法、构造函数方式、原型方式、构造函数+原型方式;

显式自定义对象

var obj1 = {
    nick: 'Byron',
    age: 20,
    printName: function(){
        console.log(obj1.nick);
    }
}
var obj2 = {
    nick: 'Casper',
    age: 25,
    printName: function(){
        console.log(obj2.nick);
    }
}

适用场合:
临时对象。比如作为方法的参数传递时,可以这样封装对象。
全局常量对象。很少做修改情况下适合这样封装对象。比如定义一个AjxxConst对象,该对象的属性是一些常量值,比如案件状态–已公开=3.

缺点:
不能很好的复用;
多个客户端并发访问时,数据容易被修改;

工厂方法封装对象

function createObj(name, age){
    var person = new object();
    person.name = name;
    person.age = age;
    person.printName = function(){
        console.log(this.nick);
    }
    return person ;
}

客户端:
var p1 = createObj('权志龙', 30);
p1.printName ();

适用场合:
创建操作比较频繁,即经常创建同一类的实例时,可以使用工厂方法。
可以理解为工厂方法是对“创建object对象”这一操作的封装。

缺点:
构造出来的对象类型都是Object,没有识别度;
创建多个实例的同时,还要创建重复的行为printName;

构造函数方式封装对象

<script>

    function Person(name, age){
       this.name = name;
       this.age = age;
       this.sayName = function(){
               console.log(this.name);
               console.log(this.age);
       }
    }

    function test(){
        var p1 = new Person();
        var p2 = new Person('权志龙');
        var p3 = new Person('权志龙',30);
        p1.sayName();//undefined,undefined
        p2.sayName();//权志龙,undefined
        p3.sayName();//权志龙,30
    }

 </script>

JS里万物皆对象,方法也是对象。因此可以通过声明一个方法来指定对象类型。

适用场合:
没有固定的场景,自己根据实际情况来决定是否使用吧。
构造方法封装对象,是不是有点儿类似java了呀,还有比这更类似的呢!

缺点:
创建多个实例的同时,还要创建重复的行为printName;

原型方式封装对象

function Person(){
}

Person.prototype.name = "权志龙";
Person.prototype.age = 30;

Person.prototype.printName = function(){
    console.log(this.name);
}

Person.prototype.printAge = function(){
    console.log(this.age);
}

客户端:
var p1 = new Person();
p1.printName();
p1.printAge();

适用场合:
实例们的属性值固定不变时,适合这样封装对象。

缺点:
参数值绑定的太死了,是不是,是不是,是不是!

构造函数+原型方式封装对象

function MyObject(otherObject){
    alert("有参构造函数!");
    this.otherObject = otherObject;
}

MyObject.prototype.name = "y0";
MyObject.prototype.age = "0";

MyObject.prototype.getName = function getName(){
    console.log(this.name);
};

MyObject.prototype.getAge = function getAge(){
    console.log(this.age);
};

MyObject.prototype.setName = function setName(name){
    this.name = name;
};

MyObject.prototype.setAge = function setAge(age){
    this.age = age;
};

MyObject.prototype.getOtherObject = function getOtherObject(){
    console.log(this.otherObject);
};

MyObject.prototype.setOtherObject = function setOtherObject(otherObject){
    this.otherObject = otherObject;
};

客户端:
    var myObject1 = new MyObject();
    myObject1.getName();//y0
    myObject1.getAge();//0
    myObject1.setName("yulq");
    myObject1.setAge(18);
    myObject1.getName();//yulq
    myObject1.getAge();//18
    myObject1.getOtherObject();//undefined

    var myObject2 = new MyObject(11);
    myObject2.getName();//y0
    myObject2.getAge();//0
    myObject2.setName("yulq2");
    myObject2.setAge(20);
    myObject2.getName();//yulq2
    myObject2.getAge();//20
    myObject2.getOtherObject();//11

适用场合:
完美。

缺点:
缺点就是太完美了!

4.定义JS变量

JS变量可以分为三种:私有变量、实例变量、静态变量。

<script>
    function ClassA(){
        var a = 1; //私有变量,只有函数内部可以访问
        this.b = 2; //实例变量,只有实例可以访问
    }
    ClassA.c = 3; // 静态变量,也就是属性,类型访问
    ClassA.prototype.d  = 4;//实例变量,只有实例可以访问

    function test(){

        console.log(a); // error
        console.log(ClassA.b) // undefined
        console.log(ClassA.c) //3
        console.log(ClassA.d) //undefined

        var classa = new ClassA();
        console.log(classa.a);//undefined
        console.log(classa.b);// 2
        console.log(classa.c);//undefined
        console.log(classa.d);//4
    }

 </script>

5.JS实现继承

JavaScript并不提供原生的继承机制,我们自己实现的方式很多,介绍一种最为通用的。
我们如果实现了两点的话就可以说我们实现了继承:
得到一个类的属性;
得到一个类的方法;

获得父类属性

    function Person(name, age){
       this.name = name;
       this.age = age;
    }
    Person.prototype.sayName = function (){
         console.log(this.name);
    }
    ---------------------------
    function SubPerson(name,age,like){
       Person.call(this,name,age);
       this.like = '唱歌';
    }
    ----------------------------
    function test(){
        var p1 = new SubPerson('权志龙',30,'唱歌');
        console.log(p1.name);//权志龙
    }

获得父类属性的原理是:
在子类构造函数里,通过Function.prototype.call(obj,param1,param2…)方法装配子类对象。
call()方法,将某个对象构造方法里的this换成第一个参数指定的对象;
因此,通过调用父类的call给子类装配父类属性,实现了继承属性。

获得父类方法

我们知道类的方法都定义在了prototype里面,所以只要我们把子类的prototype改为父类的prototype的备份就好了。
SubPerson.prototype = Object.create(Person.prototype);
我们通过Object.create()方法clone了一个新的prototype而不是直接把Person.prtotype直接赋值。因为引用关系,这样会导致后续修改子类的prototype也修改了父类的prototype,因为修改的是一个值。
这样做需要注意一点就是对子类添加方法,必须在修改其prototype之后,如果在之前会被覆盖掉。

还有个问题,我们知道prototype对象有一个属性constructor指向其类型,因为我们复制的父元素的prototype,这时候constructor属性指向是不对的,导致我们判断类型出错:
SubPerson.prototype.constructor; //Person
因此我们需要再重新指定一下constructor属性到自己的类型:
var _prototype = Object.create(Person.prototype);
_prototype.constructor = SubPerson;
SubPerson.prototype = _prototype;

最终示例如下:

 <script>
    --------------------封装父类对象-----------------------
    function Person(name, age){
       this.name = name;
       this.age = age;
    }
    Person.prototype.sayName = function (){
         console.log(this.name);
    }


    --------------------封装子类对象-----------------------
    function SubPerson(name,age,like){
       Person.call(this,name,age);
       this.like = like; 
    }

    var _prototype = Object.create(Person.prototype); 
    _prototype.constructor = SubPerson; 
    SubPerson.prototype = _prototype;

    SubPerson.prototype.sayLike = function (){
         console.log(this.like);
    }


    --------------------客户端-----------------------
    function test(){
        var p1 = new SubPerson('权志龙',30,'唱歌');
        console.log(typeof SubPerson);
        console.log(p1 instanceof SubPerson);
        p1.sayName();
        p1.sayLike();
    }

 </script>

实现要点:
1,在子类的构造方法里,通过Father.call(this,param1,param2……..)获得父类属性;
2,在扩展子类方法之前,通过Object.create(Father.prototype)克隆一个父类原型,将该原型的constructor 指向SubPerson,最后将子类原型指向克隆出来的父类原型,从而子类获得了父类方法。

发布了38 篇原创文章 · 获赞 2 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章