简介

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有逗号分隔参数
applycall的第一个参数都是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惯坏了, 总觉得简单直接的才是好的. 总想寻求最简答案.