高阶函数 是指操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数
//返回一个新的可以计算f(g())的函数
function compose(f,g){
return function(){
//需要给f()传入一个参数,所以用call
//需要给g()传入很多参数,所以用apply
return f.call(this, g.apply(this, arguments));
}
}
var square = function(x){ return x*x };
var sum = function(x, y){ return x+y };
var squareOfSum = compose(square, sum);
squareOfSum(2, 3); //5
partial
//partial
function arr(a, n){
return Array.prototype.slice.call(a, n||0);
}
function partialLeft(f /*, ...*/){
var args = arguments;
return function(){
var a = arr(args, 1); //干掉第一个参数 f, 拿剩下的
return f.apply(this, a.concat(arr(arguments)));
};
}
function partialRight(f /*, ...*/){
var args = arguments;
return function(){
var a = arr(args, 1); //干掉第一个参数 f, 拿剩下的
return f.apply(this, arr(arguments).concat(a));
};
}
function partial(f /*, ... ...*/) {
var args = arguments;
return function(){
var a = arr(args, 1); //干掉第一个参数 f, 取剩下的
var i = 0, j = 0;
//遍历args, 从内部实参填充undefined元素
for(; i<a.length; i++){
if(a[i] === undefined) a[i] = arguments[j++];
}
// 将剩下的参数追加进来
a = a.concat(array(arguments, j));
return f.apply(this, a);
};
}
var f = function(x, y, z) { return x * (y-z)};
partialLeft(f, 1)(2, 3) //1*(2-3)
partialRight(f, 1)(2, 3) //2*(3-1)
partial(f, undefined, 1)(2, 3) //2*(1-3)
memorization
function memorize(f){
var cache = {};//将值保存在闭包内
return function(){
var key = arguments.length + Array.prototype.join.call(arguments, ",");
if (key in cache) {
return cache[key];
} else {
return cache[key] = f.apply(this, arguments);
}
};
}
function sum(){
var args = Array.prototype.slice.call(arguments);
var s = 0;
args.forEach(function(v){
s += v;
});
return s;
}
var mySum = memorize(sum);
mySum(1,51,34);
- 类和原型
- 类和构造函数
P204 9-2例子 有两个问题
- 第一 r.foreach(console.log) TypeError: Illegal invocation 原因: https://bugs.chromium.org/p/chromium/issues/detail?id=48662
- 第二 最后 使用时用的是9-1的例子工厂方式
下面是修改的正确的例子
function Range(from, to){
this.from = from;
this.to = to;
}
Range.prototype = {
includes: function(x){
return this.from <= x && x <= this.to;
},
foreach: function(f) {
for(var x = Math.ceil(this.from); x <= this.to; x++)
f(x);
},
toString: function(){
return "(" + this.from + "..." + this.to + ")";
}
}
var r = new Range(1, 3);
r.includes(2);
r.foreach(console.log.bind(console));
console.log(r.toString());
- 构造函数和类的标识 原型对象是类的唯一标识;而且仅当两个对象继承自同一个原型对象时,它们才属于同一个类的实例
r instanceof Range //r如果继承自Range.prototype, 则返回true
实际上instanceof并不会检查是否是由Range()构造函数初始化而来,而是检查r是否继承自Range.prototype
- constructor属性 每个JavaScript函数(除ES5 Function.bind()方法返回的函数除外)都自动拥有一个prototype属性。这个属性的值是一个对象, 这个对象包含为一一个不可枚举的属性constructor。constructor属性的值是一个函数的对象
var F = function(){}
var p = F.prototype;
var c = p.constructor;
c === F //true F.prototype.constructor == F
var o = new F();
o.constructor == F //true
因此Range.prototype定义的话,为了防止constructor等被覆盖掉,采用下面的方式
Range.prototype.includes = function(x) {
return this.from<=x && x<=this.to;
};
Range.prototype.foreach = function(f) {
for(var x = Math.ceil(this.from); x <= this.to; x++)
f(x);
};
Range.prototype.toString = function() {
return "(" + this.from + "..." + this.to + ")";
};
在JavaScript中定义类的步骤,有三部
- 定义一个构造函数,并初始化新对象的实例属性
- 给构造函数prototype上定义实例方法
- 给构造函数定义类字段和属性
function extend(o, p){
for(prop in p){
o[prop] = p[prop]
}
return o;
}
function defineClass(constructor, methods, statics){
if(methods){extend(constructor.prototype, methods)}
if(statics){extend(constrcutor, statics)}
return constrcutor;
}
var SimpleRange = defineClass(function(f, t){this.f = f; this.t = t;},
{
includes: function(x){/*省略*/},
toString: function(){/*省略*/}
},
{
upto: function(t){/*省略*/}
});
我们可以很轻松的对原型进行改造,添加方法,这样继承原型的所有实例对象会有相应的方法
String.prototype.trim = String.prototype.trim || function(){
if(!this) return this;
return this.replace(/^\s+|\s+$/g, "");
};
Function.prototype.getName = function(){
return this.name || this.toString().match(/function\s*([^()*]\(/))/)[1];
}