一、构造函数
1.为什么要有构造函数
一些对象具有相同的属性和方法(特征和行为),将他们抽象出一个同一个类型,在JS中就需要通过一个构造函数来创建这些对象,在构造函数内部设置对象的属性和方法
好处:一次封装,多次调用,可省略一些代码,也让代码更具有可读性。
2.获取对象上的属性和方法
(1). 属性:
a. 实例.属性名;
b. 实例[“属性名”];
(2).方法:
a. 实例.方法名;
b. 实例[“方法名”];
3.设置对象上的属性和方法
(1). 属性:
a. 实例.属性名=新的值;
b. 实例[“属性名”]=新的值;
(2). 方法:
a. 实例.方法名=新的值;
b. 实例[“方法名”]=新的值;
4.删除原型对象中的say方法
delete
5.构造函数和普通函数
构造函数和普通函数只在调用方式不同
(1). 当成普通函数来调用
a. 函数内部的this指向调用的对象(如果没有找到调用的对象,this指向window)
b. 函数的返回值由return语句决定,如果没有说明函数没有返回值(返回值是undefined)
(2). 当成了构造函数来调用会经历以下过程
a. 创建一个该构造函数的实例
b. 将构造函数内部的this的值指向该实例
c. 执行函数体
d. 默认的返回值:该实例
(3). 函数调用的4种方式 function fn(){}
a. 普通调用:fn();
b. 当成构造函数调用:new fn();
c. 被对象调用:o.fn();
d. 上下文模式:call/apply
(4). 构造函数的返回值
a. 构造函数没有手动添加返回值,返回构造函数的实例
b. 构造函数返回基本数据类型的值,返回的还是构造函数的实例
c. 构造函数返回对象类型的值,返回就是那个对象
二、原型对象
1.什么是原型对象?
构造函数的prototype属性:随着实例化的次数增加,不同的对象他们拥有的say方法指向不同的内存,功能相同,造成了内存的浪费,为了解决内存,将这个方法放在某个对象(原型对象)中.
结论1:给构造函数的prototype属性(对象)添加一个方法,这个方法就可以被构造函数的实例所共享
推论1:构造函数的prototype属性(对象)上面的属性、方法都可以被构造函数的实例所共享
推论2:Student.prototype.constructor===s1.constructor
结论2:构造函数的实例有一个__proto__指向的是构造函数的prototype属性(原型对象) s1.proto===Student.prototype
总结:
a. 原型对象是构造函数的prototype属性
b. 构造函数的实例的__proto__属性指向原型对象
c. 原型对象有一个constructor属性指向构造函数本身
三、对象的属性的读取与设置
查找一个对象上的是否存在某个属性的过程
a. 查找当前对象(s1)的内存中是否定义了该属性,找到就停止查找
b. 去当前对象的__proto__属性(原型对象)中去查找是否定义了该属性,找到就停止查找
c. 如果2中没找到,就去原型对象的原型对象中去查找是否定义了该属性 s1.proto.proto…
n. 找到某个对象(是没有原型对象的:没有__proto__属性),如果这个对象中还没有,确定了无法获取该属性
四、继承
1.继承的概念
a继承自b——> a.proto===b
2. 扩展原型对象实现继承
构造函数有一个prototype属性(原型对象),通过给原型对象添加一个属性、方法,从而可以让构造函数的实例可以访问到,这种继承模型称之为:扩展原型对象实现继承。
function Person(){};
Person.prototype.age = 20;
var p1 = new Person();
由于p1是Perosn的实例,所以p1是继承自Person构造函数的原型对象。
3.替换原型对象实现继承
为什么要用替换原型对象实现继承?
当我们需要给构造函数的原型对象中添加很多个属性、方法的时候,如果一直使用扩展原型对象,多写很多冗余(重复)的代码
如何实现替换原型对象实现继承?
重新给构造函数的prototype属性赋值,值是一个全新的对象,在这个对象中添加属性、方法;要注意一定要给这个对象添加constructor属性,值指向构造函数本身
4.混入继承
混入继承的使用场景:已知对象o,o2,需要将o中的功能(属性、方法)拷贝到o2中 jQuery.extend() 方法;
for (var key in o) {//key是o的属性名称,key是字符串类型的值
//属性的值:
var value=o[key];
//设置o2中的同名属性
o2[key]=value;
}
将混入继承的模型封装成函数
/**
* 混入继承:将源对象中的属性和方法拷贝到目标对象中
* @param target 目标对象:接收数据的对象
* @param source 源对象:数据从哪个对象来
*/
function mixin(target,source){
for (var key in source) {
var value=source[key];
target[key]=value;
//相当于:
//target[key]=source[key];
}
return target;
}
jQuery中的$.extend()实现原理就是混入继承
5.原型+混入继承
function Cat(){}
Cat.prototype.extend=function(source){
//调用实现混入继承的函数往原型对象中添加属性、方法
mixin(Cat.prototype,source);
};
原型+混入继承在jQuery中也有很常见的应用:$.fn.extend()–>jQuery.prototype.extend()
6.经典继承
实现的功能:已知一个对象o,需要创建一个新的对象(o2),这个新的对象继承自对象o
经典继承的使用场景:要创建一个对象(不需要关心构造函数)新对象需要继承自另一个指定的对象
//o2.__proto__===o; 将经典继承封装成一个函数:
function create(o){
function F(){}
F.prototype=o;
return new F();//返回的就是F的实例
}
ES5(IE9以下版本不支持):Object.create()的实现原理就是源自于经典继承
在旧的浏览器中应用经典继承
//1、首先应该先检测浏览器是否支持Object.create()
if(typeof Object.create !=="function"){
Object.create=function(o){
function F(){}//一个任意的构造函数
F.prototype=o;//设置构造函数的原型对象
return new F();//返回的就是F的实例
};
}
五、原型链
1.原型链的基本概念
JS由对象组成,一个对象就有它的原型对象(proto),原型对象也有它的原型对象,一直到原型链的顶端,这样构成了一个具有链条形状的结构,称之为原型链
__proto__该属性可以被修改,但是无法被删除
下面是对象字面量的原型链图
下面是构造函数创建对象的原型链图
下面是数组对象的原型链图
一般来说,无论是对象字面量,还是构造函数创建的对象、内置对象,基本包装了类型的对象,2次原型查找(.proto)就可以找到
六、属性方法
1. Object.prototype.toString()
var obj={};
console.log(obj.toString());//"[object Object]"
console.log(Object.prototype.toString.call([]));//"[object Array]"
console.log(Object.prototype.toString.call(/abc/));//"[object RegExp]"
console.log(Object.prototype.toString.call(new Date()));//"[object Date]"
console.log(Object.prototype.toString.call(1));//"[object Number]"
//1-->转换为基本包装类型的对象
console.log(Object.prototype.toString.call("abc"));//"[object String]"
console.log(Object.prototype.toString.call(true));//"[object Boolean]"
console.log(Object.prototype.toString.call(function(){}));//"[object Function]"
function Cup(){}
var cup=new Cup();
console.log(Object.prototype.toString.call(cup));//"[object Object]"
使用Object.prototype.toString.call()只能检测内置对象的类型——>$.type
2. Object.prototype.hasOwnProperty()
用来判断方法的参数(字符串类型)是否是对象的自有属性(属性定义在对象自身的内存中——>通过原型链找到的属性就不对了)
3. Object.prototype.isPrototypeOf()
表示当前对象是否是参数的原型对象(或在参数的原型链上)
4. Object.prototype.propertyIsEnumerable()
表示参数(字符串类型)是否是当前对象的可枚举(for…in遍历)属性
5. Object.defineProperty(); //ES5诞生的
//参数1:需要定义属性的对象
//参数2:属性的名称(字符串类型)
//参数3:属性的描述符(对象)
value;
enumerable; //添加了一个不可枚举属性
writable; //添加一个不可使用赋值运算符的属性
configurable; //值为true表示该属性可以被删除,值为false表示该属性不可被删除
get; //定义一个只读(只能获取值)属性
set; // 定义一个只写属性,仅仅设置set
Object.defineProperty(o1,"length",{
enumerable:false,
value:"170"
});
6. a instanceof b
表示a是否为b(函数)的实例