Js再起之函数和集合
简介
js是functional语音, 因此一切都是函数, 函数是第一公民, 变量(其实感觉这个用常量更合理)是函数, 集合是函数, 对象是函数.
function factorial(n){ //递归调用需要函数名了. 一般都不这么定义.
if ((n == 0) || (n == 1)) return 1;
else return (n * factorial(n - 1));
}
var square = function(number) { //一般都用这个定义.
return number * number;
};
// 这个时候我调用5.square 可以吗? 试验了一下, 不可以. 那么问题来了, 如何变为可以呢? 调用者自身作为参数自动传进去.
//为了递归, 上面的函数也可以定义为:
var square = function xxx(n) { //一般都用这个定义.
if ((n == 0) || (n == 1)) return 1;
else return (n * xxx(n - 1));//这里xxx和square都可以
};
//但是上面这个定义, 执行的时候只能是square()不能用xxx()了.
function map(f,a) {
var result = [], // 创建一个新的数组
i;
for (i = 0; i != a.length; i++)
result[i] = f(a[i]);
return result;
}
var numbers = [0,1, 2, 5,10];
//下面这个调用numbers作为map函数的第二个参数了. 估计是默认参数为this
var cube= numbers.map(function(x) {
return x * x * x;
});
console.log(cube);
var pet = function(name) { //外部函数定义了一个变量"name"
var getName = function() {
//内部函数可以访问 外部函数定义的"name"
return name;
}
//返回这个内部函数,从而将其暴露在外部函数作用域
return getName;
};
myPet = pet("Vivie");
myPet(); // 返回结果 "Vivie"
function myConcat(separator) {
var result = "", // initialize list
i;
// iterate through arguments
for (i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
更简洁的函数
var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];
var a2 = a.map(function(s){ return s.length }); //老的匿名函数的做法.
console.log(a2); // logs [ 8, 6, 7, 9 ]
var a3 = a.map( s => s.length ); //新的胖箭头写法
console.log(a3); // logs [ 8, 6, 7, 9 ]
//这么干的目的很明确: 不必再面向对象了. 不用再搞this了.
神奇啊, ecmascript2015才有的键值对
map 对象
真扯淡, map和对象有毛关系.
//这语法也挺坑的.
var sayings = new Map();
sayings.set('dog', 'woof');
sayings.set('cat', 'meow');
sayings.set('elephant', 'toot');
sayings.size; // 3
sayings.get('fox'); // undefined
sayings.has('bird'); // false
sayings.delete('dog');
sayings.has('dog'); // false
for (var [key, value] of sayings) {
console.log(key + ' goes ' + value);
}
// "cat goes meow"
// "elephant goes toot"
sayings.clear();
sayings.size; // 0
set对象
奶奶的, set也是对象了. 真扯淡.
而且set有毛用啊, 数组就够了啊.
还是有点用的, set中值不重复, 可以用来去重.
函数/这货才是真正的集合. json
//第一种, 别这么干, 这是和自己过不去.
var myCar = new Object();
myCar.make = "Ford";
myCar.model = "Mustang";
myCar.year = 1969;
//第二种, 放到迭代里面有用
myCar["make"] = "Ford";
myCar["model"] = "Mustang";
myCar["year"] = 1969;
//就没有正常的吗? 正常的来了. 对象初始化器objct initializer
var obj = { property_1: value_1, // property_# may be an identifier...
2: value_2, // or a number...
// ...,
"property n": value_n }; // or a string
//上面的value可以是一个对象(json), 或者函数.
var myHonda = {color: "red", wheels: 4, engine: {cylinders: 4, size: 2.2}};
var Animal = {
type: "Invertebrates", // Default value of properties
displayType : function() { // Method which will display type of Animal
console.log(this.type);
}
}
//另外再介绍两种不常用的. 1. 函数写法
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var mycar = new Car("Eagle", "Talon TSi", 1993);
//2. object.create写法
var Animal = {
type: "Invertebrates", // Default value of properties
displayType : function() { // Method which will display type of Animal
console.log(this.type);
}
}
// Create new animal type called Fishes
var fish = Object.create(Animal);
fish.type = "Fishes";
fish.displayType(); // Output:Fishes
//相当扯淡的get和set, 相比js本来简洁的语法, 这就是往iphone上贴廉价贴纸. 实在是不想弄进来, 有兴趣自己去看吧. 太恶心了.
//更扯淡的是竟然还探讨了继承, 继承有毛用啊.
prototype
Car.prototype.color = null;//增加了一个属性.
car1.color = "black";
this
<form name="myForm">
<p><label>Form name:<input type="text" name="text1" value="Beluga"></label>
<p><input name="button1" type="button" value="Show Form Name"
onclick="this.form.text1.value = this.form.name">
</p>
</form>
精华
var square = function(number) { //一般都用这个定义.
return number * number;
};
// 这个时候我调用5.square 可以吗? 试验了一下, 不可以. 那么问题来了, 如何变为可以呢? 调用者自身作为参数自动传进去.
var Animal = { //大括号方式定义对象.
type: "Invertebrates", // Default value of properties
displayType : function() { // Method which will display type of Animal
console.log(this.type);
}
}
5.square()
//这样就成功了, 这个递归很有趣.
Number.prototype.s = function x() {
const n=this.valueOf();
if ((n < 2)) return 1;
else return (n * (n-1).s());
} ;
(5).s();
//去掉递归, 可以看这个简单的例子.
Number.prototype.r = function() {
return Math.round(this.valueOf());
};
(5.5).r(); //第一种形式
5..r(); //第二种形式, 不能有小数点了.
5.5["r"](); //第三种当做数组属性处理的形式.
//针对array更简单了.
var colors = ['red', 'green', 'blue'];
Array.prototype.s = function x() {
this.forEach(function(color) {
console.log(color);
});
}
colors.s();
因此, 我们得出结论数组=对象=集合=函数=字符串=………
其实中括号或者大括号, 随便啦. 都是一样的.
中括号, 大括号, 小括号
var arr = [element0, element1, ..., elementN]; //这个和大括号方式一致.
var emp = [];
emp[0] = "Casey Jones";
arr["length"]; 等价于: arr.length
myCar.make = "Ford";
myCar["make"] = "Ford";
var obj = { property_1: value_1, // property_# may be an identifier...
2: value_2, // or a number...
// ...,
"property n": value_n }; // or a string
回调函数的参数传递
愚蠢的错误
fs.readFile('station.js', filerea(err, data));
上面是个愚蠢的错误, js对待函数像对待变量一样的, 不要传参数啊, 这个地方就是要一个函数指针, 不需要指明他的参数情况
fs.readFile('station.js', filerea);
如何传递参数是个技术活, 至少有7个办法, 都会了, 就可以召唤神龙
//包在外边
setTimeout(
function(){
httppre(json[i].stationName, json[j].stationName, '2017-12-16')//这里写多少的参数都可以.
}, 1000
)
//包在里面
function fun2(objectP){
return function(){
// 这里开始干正事, 那个参数ObjectP都可以用了.
}
}
setTimeout(fun2(p), 100);
//如果是字符串作为参数, 可以不那么麻烦, 问题来了, 多个参数咋办?
setTimeout("fun1('" + paramenter + "')", 100);
//这种形式不是太好, 因为其实是一个eval
//更完美的解决方案: Function.prototype.bind()
setTimeout(函数名.bind(null, topicId), 4000);
第一个参数, 可以传递一个函数指针(比如this)进去, 他指明了this的指向.
//第三个参数可以作为第一个参数(函数)的参数
setTimeout(alert, 1000, hello);
setTimeout(yourFunctionReference, 4000, param1, param2, paramN);
//匿名函数也可以直接带参数, 但是这种写法会导致括号套括号, 这个写法和前面的[第三个参数]其实是一样的.
setTimeout(function(参数) { //做点啥
}, i++ * 1000, 参数);
setTimeout(() => console.log(testObject[propertyName]), i++ * 1000);
//还可以用apply解决, 有兴趣的可以去研究下.
apply
这个绕不过去, 必须研究下…..
Function.prototype.apply()
fun.apply(thisArg, [argsArray])
thisArg
在 fun 函数运行时指定的 this 值
指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象)
值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的自动包装对象
argsArray
一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 fun 函数。
如果该参数的值为null 或 undefined,则表示不需要传入任何参数。
可以使用类数组对象。
apply 和 call的区别:
1. apply用数组做参数
2. call有逗号分隔参数
apply和call的第一个参数都是this, 因此, 如果只传一个参数, 那么就会被当做this吃掉
使用apply的理由:
function product(name, value)
{
this.name = name;
if (value >= 1000)
this.value = 999;
else
this.value = value;
}
function prod_dept(name, value, dept)
{
this.dept = dept;
product.apply(this, arguments); //这时把this穿了进去, 直接使用函数就好, this没有问题.
}
prod_dept.prototype = new product();
// since 5 is less than 1000 value is set
var cheese = new prod_dept("feta", 5, "food");
// since 5000 is above 1000, value will be 999
var car = new prod_dept("honda", 5000, "auto");
另一个例子
var obj = {a: "apply"};
func.call(obj, "parameter");
function func(para) {
if(this.a === "apply"){
console.log('apply');
}
}
总结: 主要是this的使用, 是函数的上下文(环境)参数作用域的问题.
顺便介绍下spread语法: var max = Math.max(...arr);//这个语法导致max可以直接接受array作为参数.
为啥要用bind?
bind是异步的, 例如:
function MyObject(element) {
this.elm = element;
element.addEventListener('click', this.onClick.bind(this), false);
};
MyObject.prototype.onClick = function(e) {
var t=this; //do something with [t]...
//without bind the context of this function wouldn't be a MyObject
//instance as you would normally expect.
};
apply call bind 欢聚一堂
var obj = {
x: 81,
getX: function() {
return this.x;
}
};
alert(obj.getX.bind(obj)());
alert(obj.getX.call(obj));
alert(obj.getX.apply(obj));
//总结一下: 如果我们使用this作为环境上下文变量, 那么我们回到之前的问题, 异步参数的解决方案就可以更简单直接, 这恐怕也是使用apply, bind, call的原因.
关于apply还有两篇好文章:
- http://javascriptissexy.com/javascript-apply-call-and-bind-methods-are-essential-for-javascript-professionals/
- https://odetocode.com/blogs/scott/archive/2007/07/04/function-apply-and-function-call-in-javascript.aspx
可惜我已经被Stack Overflow惯坏了, 总觉得简单直接的才是好的. 总想寻求最简答案.