js+面向对象相关笔记(二)

1.多态
◆多态是在强类型的语言中比较常用,javascript中没有相应的体现。


2.创建对象的方式
◆使用对象字面量的方式,如var obj={"name":"xm"};或var obj={name:"xm"};,使用字面量的方式只能够创建一次对象,复用性较差,如果要创建多个对象,代码冗余度太高了。
◆使用内置构造函数的方式加对象的动态特性.或[],var obj=new Object();,obj.name="xm";◆
◆封装简单工厂函数的方式来创建对象,实际原理也是是用内置构造函数的方式加对象的动态特性.或[],function createSong("name"){
var obj=new Object();, obj.name=name;
return obj;
}
◆自定义构造函数 Object Array,构造函数其实也是函数,但是通常用来初始化对象,并且和new关键字同时出现,new 是用来创建对象的,构造函数是用来初始化对象的,构造函数名,首字母要大写,用来区分普通函数【
//定义构造函数
function Person(){
//默认隐含的操作,把new 新创建出来的对象赋值给this,也就是下面的p赋值给this
this.name="尼古拉斯豆瓣酱";
this.age=50;
this.sayHello=function(){
console.log("hey girl");
}
}
//创建对象
var p=new Person();
console.log(p);
console.log(p.name);
//如果构造函数没有参数的时候可以这样创建对象
var p2=new Person;
console.log(p);
console.log(p.name);
】。
◆构造函数执行的过程【
◇使用new关键字创建对象
◇使用构造函数,把新创建出来的对象赋值给构造函数内的this关键字
◇在构造函数内使用this为新创建出来的对象新增成员
◇默认返回新创建的这个对象
◇如果自己写return语句,return后面写的是空值(return ;)或者是基本类型的值(return 123;),那么还是会返回新创建的这个对象
◇如果自己写return语句,return返回的是object类型的值(除了null),那么就不会返回原来那个新创建的对象,而是返回你return的那个objcet类型的值。
☆无论return返回的是undefined还是null,都会返回原来那个新创建的对象,因为undefined==null为true,但是undefined===null为false,它们还是有区别的,因为undefined是基本数据类型的,而null是object类型的,并且null转换为数字类型的值为0,而undefined转换为数字类型的值为NAN(不是一个数字,无法进行运算和转换)。




3.js中提供了两个方法来调用其它对象的方法
◆call
◆apply


4.检测一个对象是Object类型还是Array类型
◆var obj=new Object();var arr=new Array();,
obj.toString();//[object Object]
Object.prototype.toString.call(arr);//[object Array],直接获取最后面的数据,使用字符串.slice(8,-1),来获取真正数据类型的字符串,即可判断是Object类型还是Array类型。
◆Object.prototype.toString.call()可以用来检测具体的数据类型,但是只能用来检测系统已经定义好的对象的数据类型,如果是自己自定义的构造函数类型则不能检测。


5.如果不使用new关键字直接调用自定义的构造函数,那么自定义的构造函数中的this关键字会指向全局静态对象window。


6.传统构造函数的问题
◆在构造函数中定义的行为是私有,每创建一个对象,都会开辟一块儿新的空间来存这私有的行为,如果创建了多个对象,它们私有的行为是一样的,那么会开辟很多块儿空间来存取这个一模一样的行为,这样会造成了资源的浪费【
//定义构造函数
function Person(){
this.name="尼古拉斯豆瓣酱";
this.age=50;
this.sayHello=function(){
console.log(this.name+"hey girl");
}
}
var p1=new Person();
var p2=new Person();
console.log(p1.sayHello==p2.sayHello);//false

◆解决问题的方式是,定义一个全局的函数,然后让内部的行为等于这个函数【
function sayhi(){
console.log(this.name+"hey girl");
}
function Person(){
this.name="尼古拉斯豆瓣酱";
this.age=50;
this.sayHello=sayhi;
}
】,虽然这样做可以,谁调用这个函数this就会执行谁,虽然很好解决了资源的浪费的问题,但同时也造成了全局变量的污染,代码也变得不清晰了,而且维护的时候有点麻烦。


7.使用原型来解决传统构造函数的问题
◆构造函数实际也是函数,函数在js预解析阶段就会被创建并且还会被提升到最上面。
◆当构造函数被创建出来的时候,系统会默认的帮构造函数创建并关联一个神秘的对象,这个对象就是原型,并且这个对象默认是一个空的对象。
◆原型中的属性和方法可以被该构造函数创建出来的对象使用,也就是该构造函数中的全局的行为或属性,可以被该构造函数创建出来的所有对象访问,但是并不是静态方法哟,可以使用构造函数.prototype来调用,也可以使用通过构造函数创建出来的对象来调用,私有的属性和方法优先级高于使用原型定义的属性和方法,所以如果出现同名时,会优先调用私有的属性和方法。
◆给原型对象添加属性和方法可以通过对象的动态特性.和[]的方式【
//定义构造函数
function Person(){
this.name="尼古拉斯豆瓣酱";
this.age=50;
//私有的方法
this.sayHello=function(){
console.log(this.name+"hey girl");
}
}
//使用原型来添加方法
Person.prototype.sayHello=function(){
console.log("hi boy");
}
Person.prototype.exercise=function(){
console.log("强身健体,保卫祖国!");
}
//调用
var p=new Person();
p.exercise();//使用构造函数创建的对象来调用原型添加的方法
p.sayHello();//如果自己的构造函数中有这个方法,那么就不会调用通过原型来添加的同名方法

◆ 使用原型来解决传统构造函数的问题,注意顺序,你调用的是使用原型添加的属性和方法,如果你调用位置是在原型添加该属性或该方法的前面,那么是无法调用的,因为该属性和该方法还没有添加进去,就像使用var fn=function(){console.log("hi girl")};,如果你调用fn()是在定义fn之前是没办法正确执行的。




8.混合式继承(mix-in)
◆ 使用for in加js里对象的动态特性[],原理是将一个对象中所有的属性拷贝一份给另一个对象,很像是一个父亲将自己的财产与儿子共享

var obj0={name:'jack',age:22,sex:'男',sayhi:function(){console.log("hi girl");}};
var obj1={};
for(var key in obj0){
//将obj0中的所有属性和方法都拷贝一份给obj1
obj1[key]=obj0[0];
}



9.使用原型的注意事项
◆使用对象来调用方法或者属性时,如果自己的内部(每创建一个对象都会开辟一块儿空间)中有这个方法名称的方法或属性名称的属性,那么就不会去调用通过原型的方式来添加的同名方法或同名属性

◇当使用对象去访问属性和方法的时候
◇会首先在对象自己内部进行查找,如果找到了,就直接使用自己内部的。
◇如果没有找到,就去原型中(prototype)查找,查找到之后,就使用原型中的
◇如果原型中没有,假如是属性,就是undefined
◇如果原型中没有,加入是方法,就会直接报错
◇当然如果原型和自己内部都没有,那么假如是属性就是undefined,那么假如是方法就会直接报错。
】。
◆如果改变了构造函数的原型的指向地址,那么之前通过实例化创建的对象的原型的指向地址则不会是你改变后的原型地址,而是你改变之前的原型地址,只有有对象引用之前的那个原型地址,那么之前的原型地址就不会被回收掉
◆js中原型的机制,每当你通过构造函数实例化出一个对象,那么这个对象就会拥有一个半静态半私有化的原型空间,你可以通过对象直接调用这个原型空间里存取的方法和属性,并且当你的构造函数中的原型prototype指向的地址没有发生改变的情况下,你可以通过构造函数的原型来扩充该构造函数的实例的属性和方法,并且该构造函数的实例都可以访问这块儿静态的原型空间,但是如果一旦构造函数的原型prototype指向的地址发生了变化,那么你之前通过构造函数创建的对象中的原型空间就会变成私有化的,自然与该构造函数的原型prototype的新原型空间无关,后续使用该构造函数实例化创建的对象的新原型空间自然也不会和之前的旧原型空间有关联。
◆使用对象访问属性的时候,如果在构造函数内找不到,那么就会去原型中去查找,但是,使用点语法进行赋值的时候,并不会去原型中进行查找,使用点语法赋值的时候,如果构造函数中不存在该属性,那么就会该对象新增该属性,因为这是对象的动态特性,所以并不会去修改原型中的该属性,至少不能直接这么去改原型中的属性。
★实际上以上说的半静态和半私有化只是一种理解,因为构造函数的原型中的方法和属性就是静态的,不过没有那个真静态,因为还要注意顺序,不然调用不了,并不像其它强类型语言中,先加载你书写好的所有静态成员,于是如果你调用的时候是在加载之前则无效,而且如果你改变了原型的指向地址,那么你的构造函数原型就会发生改变,之前添加的原型中的方法都会被清空,并且与新原型关联的构造函数变成了一个系统内置的构造函数Object了。


10.实例化:通过构造函数创建对象的过程就叫做实例化。


11.访问原型的两种方式:prototype和__proto__
◆通过构造函数来访问原型:【
 Person.prototype

◆通过对象来访问原型:【
var p=new Person();
p.__proto__
但是这个属性并不通用,不是w3c组织的标准,所以不推荐这么使用,但是一些特别老的浏览器只支持对象访问原型__proto__而不支持构造函数访问原型prototype,当然有些高版本的浏览器也支持__proto__,但是一般用来进行测试。



12.原型对象的构造函数 constructor
◆原型对象prototype在创建出来的时候,会默认有一个属性,这个属性就是constructor,并且这个constructor指向的就是与这个原型对象关联的构造函数。
◆可以通过对象.constructor来获取该对象的构造函数,虽然到这里特别绕,但是只需要知道,构造函数与原型关联,原型是构造函数的一个属性(prototype),同时构造函数也是原型的一个属性(constructor)。
◆可以使用constructor做很有趣的事情【
function Person(name.age,action){
this.name=name;
this.age=age;
this.action=action;
}
//创建一个对象
var p=new Person("艾斯",25,function(){console.log("艾斯火拳头!!!");});
p1.action();


//使用constructor来创建新的对象
var p2=new p.constructor("杰斯",23,function(){console.log("杰斯超拳头!!!");});
p2.action();





13.使用新的对象替换掉默认的原型对象之后,原型对象中constructor属性会变成Object的构造函数,为了保证这个原型的构造函数与原型及对象之间的三角关系的合理性,应该做如下操作【
在替换原型对象的时候,在新的原型对象中手动添加 constructor属性为当前原型的构造函数,

//定义的构造函数
function Person(){}
console.log(Person.prototype.constructor);//function Person(){}

//替换构造函数的原型后
Person.prototype={};
console.log(Person.prototype.constructor);//function Objection(){}


//替换构造函数的原型后手动添加constructor属性
Person.prototype={
constructor:Person
};
console.log(Person.prototype.constructor);//function Person(){}

posted @ 2018-06-15 09:53  我叫贾文利  阅读(74)  评论(0编辑  收藏  举报