🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[toc] ## 基于面向对象创建数据值 ### 引用数据类型的两种创建方式 数组 ``` var ary = [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({name:'克丽丝'}); //没意义 //我们一般用new的方式创建空对象 var obj = {name:'克丽丝'}; var obj2 = new Object(obj); obj2 === obj; ``` ### 基本数据类型的两种创建方式 ``` //=>字面量创建出来的是一个基本数据类型值(但也是Number的一个实例,可以调取Number赋予它的方法) var num = 12 typeof num => number //=>构造函数方式创建出来的也是Number的一个实例(也可以使用Number赋予它的方法),但是获取的结果是对象数据类型的 var num = new Number(12); typeof num => object ``` 通过字面量创建出来的实例是基本类型的值,这体现了JS的松散型(严谨的面向对象只能通过new的方式) 两种方式创建出来的都是类的实例 ,当一个通过字面量创建的数据想要调用一个方法时,基本类型会变成一个真正的实例对象,再调用类上的方法 ### 构造函数设计模式(constructor) 使用构造函数方式,主要是为了创建类和实例,也就是基于面向对象编程思想来实现一些需求的处理 在JS中,当我们使用`new xxx()`执行函数的时候,此时的函数就**不是普通的函数了**,而是变为一个**类**,返回的结果叫做当前类的实例,我们把这种`new xxx`执行的方式称之为`构造函数设计模式`。 ``` function fn(){ } fn(); //加了new 就不再是普通函数运行 var f = new fn(); //不一定要大写,只是不够严谨,最好还是将类名的第一个字母大写 //f是当前这个类的一个实例 ``` #### 普通函数执行 VS 构造函数执行 构造函数执行,即具备普通函数执行的一面,也同时具备自己独有的一些操作。 在构造函数执行期间,浏览器默认创建的对象(也就是函数体中的this),代表的就是当前这个类的一个实例,浏览器会把默认创建的实例返回,所以我们说:new Fn()执行,Fn是一个类,返回的结果是Fn这个类的一个实例。 >普通函数执行 >1、 开辟一个新的私有作用域 >2、 形参赋值 >3、 变量提升 >4、 代码自上而下执行(return后面的值就是当前函数返回的结果) >5、 栈内存释放或则不释放 ``` 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、 在私有作用中完成类似于普通函数的操作:形参赋值以及变量提升 >3、 在代码自上而下执行之前,构造函数有属于自己比较特殊的操作:浏览器会在当前作用域中默认创建一个对象数据类型的值,并且会让当前函数中的执行主体(this)指向创建的这个对象 >4、 向普通函数一样,代码自上而下执行:当遇到this.xxx = xxx 这里的操作都是在给浏览器帮我们创建的这个对象增加属性名和属性值 >5、 代码执行完成后,即使函数中没有写return,在构造函数模式中:浏览器会默认把创建的对象返回到函数外面 ``` function Fn(num){ //this的第四种情况 //=>在构造函数模式中,方法体中出现的THIS是当前类的一个实例(this.xxx = xxx都是在给当前实例增加一些私有的属性) this.num = num; } var f = new Fn(10); ``` #### 深入理解构造函数执行的步骤 >当构造函数或则类,执行的时候不需要传递任何的实参值,此时我们是否加小括号就不重要了。(不传递实参的情况下,小括号可以省略) > >构造函数执行,同时具备了普通函数执行的一面,也有自己特殊的一面,但是和实例相关的,只有自己特殊的一面才相关(也就是this.xxx=xxx才相当于给当前实例增加了私有属性),函数体中出现的**私有变量**,和实例都**没有**直接的关系 > >通过类创建出来的每一个实例都是单独的个体(单独的堆内存空间),实例和实例之间是不相同并且独立互不影响的。(市面上部分开发把这种模式叫做单例模式,这种说法是不确切的,JS中的这种模式叫做构造函数设计模式) > >在构造函数体中,通过this.xxx给实例设置的属性都是当前实例的私有属性 ``` function Fn(){ var num = 111; this.name = 'ahh'; this.sum = function(){} } var f1 = new Fn(); var f2 = new Fn(); f1.num //->undefined f1.name //->ahh //=>不同实例是不同的空间地址 f1===f2; //false f1.sum === f2.sum; //false ``` ![](https://box.kancloud.cn/b3a6e97015da68e1f001aac43c89d7a6_1409x737.png) ##### 关于return >当构造函数体中我们自己手动设置了`return`(默认返回的是实例:对象类型值),return的值是一个基本类型值,对最后返回的实例没有任何的影响,但是如果返回的是引用数据类型的值,会把默认返回的实例替换掉。 ``` function Fn(){ this.name = 'ahhh'; return 1 } var f = new Fn(); //f =1 function Fn(){ this.name = 'ahhh'; return {name:'xxx'} } var f = new Fn(); //=>f不再是Fn的实例,而是变为手动返回的对象了 ``` #### instanceof >用来检测当前实例是否隶属于某个类 ``` function Fn(){} var f = new Fn; console.log(f instanceof Fn); //=>true ``` >instanceof解决了typeof无法识别是数组还是其他引用类型数据的问题 ``` [] instanceof Array //=>true /^$/ instanceof Array //=>false ``` >所有对象数据类型的都是`Object`类的实例(最终都会`__proto__`指向Object.prototype) #### hasOwnProperty VS in in:用来检测当前这个属性是否隶属于对象(不管对象是私有的还是公有的属性,只要有返回就是true) hasOwnProperty:检测当前属性是不是属于当前对象的私有属性(不仅要是对象的属性,而且需要是私有的才可以,或则说自己有但祖先也有) ```javascript var obj = {name:'ahhh',age:111}; 'name' in obj; //=>true 'toString' in obj; //=>true 'hasOwnProperty' in obj; //=>true //hasOwnProperty是object这个内置类中提供的属性方法,只要当前对象是Object的一个实例,就可以使用这个方法 obj.hasOwnProperty('hasOwnProperty'); //=>false obj.hasOwnProperty('name'); //=>true ``` >检测一个属性是否是当前对象的公有属性 >1、 是对象的一个属性 >2、 不是对象的私有属性 ``` function isAncestorProperty(attr,obj){ return (attr in obj)&&(!obj.hasOwnProperty(attr)); } ```