对象之间的继承#1
已有的属性保留,没有的属性继承。
封装一个对象之间拷贝继承的函数:
function extend(parent,child){
for(var i in parent){
if(child[i]){
continue;
}
child[i] = parent[i];
}
}
原型继承#2
完整的继承,应该是
原型链上层的类是更广泛的类,下层的类作为上层的分化,其中含有一部分公共属性是从上层继承而来。
方法:将上层构造函数创建的实例对象赋值给下层的原型对象
e.g. Lower.prototype = new Upper(a,b,c);
这样,Lower 生成的实例是可以调用到 Upper 的属性的。
此方法存在的问题:
- 需要重定向 constructor
Lower.prototype.constructor = Lower; - 继承的值会相同
函数的 call 方法#3
call 是一种执行函数的方法,它的作用有两个:
- 更改函数内部的 this 指向
- 调用函数执行内部代码
详见【#10】
e.g.
afunc.call(toThis,a);call 方法第一个参数用来指定内部 this 指向,第二个及以后的参数为函数原本的参数。
借用构造函数继承属性#4
进一步:
将上层的构造函数写进下层的构造函数,同时使用 call 方法修改上层构造函数的 this 指向下层构造函数的 this(不修改的话,指向的是window)。
function Lower (a,b,c,s) {
Upper.call(this,a,b,c);
this.special = s;
}
构造函数方法的继承#5
前述的内容只完成了属性的继承,上层原型对象的方法也需要继承。
同样的,方法的继承也有两种做法。
- 对象拷贝继承
除 constructor 以外的原型方法都复制过来。for (var i in Upper.prototype) { if(i === constructor){ continue; } Lower.prototype[i] = Upper.prototype[i]; } - 原型继承(同【#2】)
组合继承#6
属性使用构造函数继承,方法使用原型继承
【新小结】函数定义方式#7
函数的定义方法:
-
函数声明
function f(){}; -
函数赋值
var f = function (){};二者区别在于当函数调用语句写在之前时,声明的会因函数声明提升而正常调用,而赋值的只有变量声明被提升未被赋值,调用
undefined出错。
因此,推荐使用函数声明。但是,if 语句中的函数声明存在兼容性问题
if(true){ function f(){ft()}; }else{ function f(){ff()}; }现代浏览器会认为是函数赋值,低版本浏览器则是函数声明
前调用,现代:undefined;低版本:ff()(后被提升者)
后调用,现代:ft()(符合逻辑);低版本:ff()
因此,此类情况使用函数赋值:var f; if(true){ f = ft; }else{ f = ff; } -
【新】使用构造函数创建#8
var f = new Function();
新建函数的 形参 和 内部代码 均以字符串形式传入构造函数
e.g.var func = new Function('a','b','console.log(a+b)');
得到的函数为:function (a,b) {console.log(a+b)}【注意】不推荐使用
【小结】函数的调用和 this 的指向#9
- () 运算符执行;this 默认指向 window
- new 关键字调用,并加 () 执行;this 指向将来创建的实例对象
- 对象中的方法,打点调用,同时也得加 ();this 指向对象
- 事件函数在被触发时自动执行;this 指向事件源
- 定时器、延时器中的函数在规定的时间自动执行;this 指向 window
this 的指向与函数的执行方法密切相关,具体情况具体分析。
指定函数内 this 的三个方法#10
- call
- 功能:
- 参数:第一个指定 this,第二个及以后的是函数的实参
- apply
- 功能:指定函数的 this,并调用
- 参数:第一个指定 this,第二个是函数实参组成的数组
- bind
- 功能:指定函数的 this,不执行函数
- 参数:第一个指定 this,第二个及以后的是函数的实参
- 返回值:返回了重新指定了 this 的新函数
func.call(newThis);
func.apply(newThis);
func.bind(newThis)();
这三个方法的使用效果一样,各自有对应的应用。
call 的应用#11
常用于类数组的对象。
e.g.借用数组的方法来处理对象:
Array.prototype.push.call(aObj,50);
其中,aObj 是一个对象
apply 的应用#12
多用于数组,省去拆分数组的麻烦。
e.g.将对象的 Math.max 方法应用到数组
Math.max.apply(null,arr);
Math.max 方法本是需要多个参数,现在利用apply 直接对数组 arr 求最大值。
第一个参数传 null,表示不修改 this。
e.g.
console.log.apply(null,arr);
将数组 arr 以单个的形式在控制台输出。
bind 的应用#13
多用于不想执行函数的情况。
e.g.修改定时器内部的函数
setInterval(function(){}.bind(this),1000);
函数的其他成员#14
- arguments【常用】
由所有实参组成的一个类数组
它是一个关键字,在函数内可以直接使用,省略function.
arguments 下也有属性:- callee 指向函数自身
- length 实参个数
- caller
函数的调用者
函数在哪个作用域调用,caller 就指向谁
全局作用域下调用,caller 为 null - length 形参的个数
- name 函数名
高阶函数#15
能够作为其他函数的 参数 或 返回值 的函数,便是高阶函数。
函数闭包(Closure)#16
函数定义时的作用域和函数自己所形成的一个密闭环境,称为闭包,包含了函数本省和它需要的变量。
不论函数以任何方式在任何地方进行调用,都会回到闭包内执行。
闭包内的变量能够因函数的调用而改变。
闭包的用途/好处:
- 在函数外部读取函数内部成员
- 让函数内成员因闭包的保留而始终活在内存中
闭包的问题:
- 循环体中的函数,结束后调用,不能记住当时的中间值,只能是循环最终值
解决办法:
将循环体内部的函数改为自调用函数,并将参数传入,那么它的闭包为自调用内部,只跟传入的实参有关,而非循环的变量。