new关键字的作用
1.new会在内存中创建一个新的空对象
2.new会让this指向这个新的对象
3.实例化构造函数里面的代码 目的:给这个新对象加属性和方法
4.new会返回这个新对象 (所以构造函数里面不需要return)
原型链
所谓的原型链,就是指prototype。
简单来说,我们实例化了一个对象,你实例的这个对象肯定需要有一个东西指向它的原型吧,这个东西就是prototype,prototype是每个函数都有的,如果是对象,需要用proto。
还记得面向对象的最基本思路吗:属性私有,方法公用。
当我们创建一个构造函数的时候,构造函数里面只应该有属性,方法应该全部定义到prototype上,这跟js的内存使用有关系,当我们实例化一个对象的时候,你new的构造函数中的值,都会再内存中复制一个并占用空间,但是prototype中的方法,只有一个引用,并不会占用空间。这就是prototype的作用。
prototype其实是一个对象。
你打印它的时候会发现,它里面的constructor其实就是你的原型。
举个例子:
// 创建一个原型对象 这个方法也叫做构造函数
// 构造函数的目的是为了解决当你有多个相似对象的使用,可以创建一个公用的源模型,然后从这个基础之上再开发
var model = function(canshu){
this.num1 = canshu;
//你可以把方法定义到这个里面,但是这个方法属于每个实例的私有方法,它是可以被更改的。
this.fun = function(){
console.log(canshu);
}
}
//console.log(model.num1); //undefined 你不能直接打印原型对象的值
// 所谓的原型链就是给源模型定义方法的地方,这个方法是公共方法,每个被实例的原型都可以拥有
// 它跟php中public和static是一样的
model.prototype.publicFun = function(){
console.log(this.num1+1); //2
}
var demo = new model(1);
console.log(demo.num1); //0
demo.publicFun(); //2
继承
所谓的继承,就是我有俩class,或者你可以理解为构造函数,现在我需要用后面这个获取前面那个的一些属性和方法。
其实我只需要在第二个里面修改一下this指针的指向就行了。
var class1 = function (){
this.name = 25;
}
class1.prototype.fun1 = function(){
console.log("我是class1上的方法");
}
//开始使用class2继承class1
var class2 = function (age){
//在es5中的继承 其实就是在构造函数内部改变了指针的位置
//但是这样只能继承属性,如果你想继承方法请往下看
class1.call(this,name);
this.age = age
}
//继承class1中的方法
//class1.prototype = class2.prototype 这样并不友好,我们创建一个空方法复制并获取class1
var f = function(){}
f.prototype = class1.prototype
class2.prototype = new f()
var sl = new class2(11);
console.log(sl.name);
sl.fun1();
这里需要注意以下,当你需要复制prototype上面方法的时候,需要创建一个空方法再复制。
拷贝对象
这里有一个误区,就是如果我让你把a里面的值,赋值给b,你会怎么做,是:
var b = a
这样吗,如果你这样写,你就上当了。这样只会把b的this指针指向a,但是并没有真的复制一个对象。
你应该这样做:
//深复制对象方法
var cloneObj = function (obj) {
var newObj = {};
if (obj instanceof Array) {
newObj = [];
}
for (var key in obj) {
var val = obj[key];
//newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; //arguments.callee 在哪一个函数中运行,它就代表哪个函数, 一般用在匿名函数中。
newObj[key] = typeof val === 'object' ? cloneObj(val): val;
}
return newObj;
};
//测试
var obj = {
a:function(){
console.log(this.b.c)
},
b:{c:1},//设置一个对象
},
newObj = cloneObj(obj);//复制对象
newObj.b.c=2;//给新对象赋新值
obj.a();//1,不受影响
newObj.a();//2
使用for in去遍历一下要clone的对象,然后再重新赋值。
这里有人曾经提出了一个深拷贝和浅拷贝的问题,
//prototype是给方法 设置属性的地方
obj.a.prototype.a = function(){
console.log(123);
};
//obj.a.prototype.a(); //123
newObj = cloneObj(obj);//复制对象
//newObj.a.prototype.a(); //123 深拷贝可以实现对prototype的拷贝
我认为的深拷贝是能够把再prototype上的内容也拷贝到新得对象上得拷贝,可见通过for in是可以实现的。
闭包
我们不想每次都声明一大堆全局变量—首先大量的全局变量不符合模块化精神,也容易造成变量污染。
那么我们能不能把值保存在函数体内部呢?(你也可以理解为用局部变量去储存值,用的时候从局部变量去读取)
必然可以!不过,我们需要一个函数来连接函数体内部的值,这个方法就叫闭包。(我过去一度被闭包这俩问题所迷惑,现在看来它就是一个函数,类似于你操作vuex中的方法)
请看例子:
var nums = {
num:0,
add:function(){
return this.num+=1;
}
}
console.log(nums.add());//1
console.log(nums.add());//2
console.log(nums.num);//2
当我们需要使用的时候,读取一下即可,修改的时候,执行我们闭包中的方法就ok了。
面试考这个的时候,就是为了看你对vuex和redux的理解。
apply/call和bind函数
其实这三个函数都是一样的作用,用于修改函数体内部this指针的指向。
你最好别去看官方的解释,你没做过项目的话肯定会一头雾水。
我们直接看例子:
添加商品的例子比如我想做一个上架商品加减个数的效果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>apply和call</title>
</head>
<body>
<div>上架商品</div>
<div>香蕉:<span id="mynum">0</span>个</div>
<button onclick="shangpin.add()">+</button>
<button onclick="shangpin.del()">-</button>
<script type="text/javascript">
var shangpin = {
pinzhong:{
xiangjiao:{
num:0
}
},
suanfa:{
add:function(){
return this.num+=1;
},
del:function(){
return this.num-=1;
}
},
dom:function(){
document.getElementById("mynum").innerHTML = this.pinzhong.xiangjiao.num;
},
add:function(){
//apply方法,可以把当前对象的this指针,修改为指向其他对象
//目的就是为了调用方法,让数据和逻辑分离
//apply[thisobj,[参数1,参数2]] //如果你需要传递参数,写到后面的array中
//apply call和bind作用完全一样,区别就是bind返回一个函数,需要再调用一次
this.suanfa.add.apply(shangpin.pinzhong.xiangjiao);
this.dom();
},
del:function(){
this.suanfa.del.apply(shangpin.pinzhong.xiangjiao);
this.dom();
}
}
</script>
</body>
</html>
我们用实现mvc的方式把数据和逻辑分开,你会发现,这里的suanfa里面,因为es5函数作用域的关系,他并不指他本身,他指的是shangpin这个大的父级类,所以我们需要把的定义到父级的数据层中去。
这里就需要修改this指针的了。
于是就用到了apply和call。当然你用bind也是一样的作用。只不过bind返回一个函数,你还得再后面再执行一下才行。
网友评论