javascript 精粹
方法定义函数
Funtction.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
}
语法
注释用// 避免一些错误
例如
/*
var rm_a = /a*/.match(s)
*/
字母开头 加上字母,数字,下划线
JavaScript 只有一个数字类型 为64位浮点数
NaN 表示不能产生正常的运算结果, 可以用isNaN(number)检测NaN
Infinity 表示 大于 1.7969313482315704+308的值
\ 反斜杠 是转义字符
所有字符都是Unicode (16位字符集)
当做假(falsy)
false
null
undefined
空字符串 ‘ ’
数字 0
数字 NaN
for in 语句 枚举一个对象的所有属性
运算符优先级
运算符 |
说明 |
. [] () |
提取属性与调用函数 |
delete new typeof + - |
一元运算符 |
* / % |
乘法 除法 求余 |
+ - |
加法/链接 减法 |
>= <= > < |
不等式运算 |
=== !=== |
等式运算符 |
&& |
逻辑与 |
| | |
逻辑或 |
? : |
三元 |
创建新对象的表示法
函数字面量定义函数值,可以有一个可选的名字,用于递归调用自己
对象
JavaScript 简单数据类型 数字、字符串、布尔值、null值和undefined
其他所有值都是对象。
一个对象字面就是包围再一堆花括号中的零或多个“名/值"对。
var empty_object = {};
var stooge = {
"first-name": "Jerome",
"last-name": "Howard"
}
检索对象里包含的值
[] 后缀中括住一个字符串表达式的方式
stooge["first-name"]
flight.departure.IATA
对象里的值可以通过赋值语句来更新
如果不存在会扩充到该对象中
stooge['middle-name'] = 'Lester'
对象通过引用来传递,它们永远不会被复制
var x = stooge;
x.nickname = 'Curly';
var nick = stooge.nickname;
每个对象都链接到一个原型对象,并且它可以从中继承属性。
所有通过字面量对象创建的对象都连接到 Object.prototype,它是JavaScript标配对象
if(typeof Object.beget !== 'function') {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
}
}
typeof 确定对象的属性
typeof flight.toString // 'function'
typeof flight.constructor //'function'
hasOwnPerty 不检查原型链 如果对象拥有独有的属性 返回true
flight.hasOwnProperty('number') // true
flight.hasOwnProperty('constructor') //false
for in 遍历一个对象的所有属性 用hasOwnProperty 或者typeof 来排除函数
var name;
for (name in anoter_stooge) {
if (typeof anoter_stooge[name] !== 'function') {
document.writeln( name + ': ' + anoter_stooge[name]);
}
}
delete 运算符可以用来删除对象的属性。如果对象包含该属性,那么该属性就会被移除。它不影响原型链中的任何属性。
another_stooge.nickname // 'Moe'
//删除 anoter_stooge 的nickname 属性, 从而暴露出原型的nickname属性
delete anoter_stooge.nickname
another_stooge.nickname // ‘Curly'
全局变量削弱了JavaScript的灵活性,应该避免使用。
最小化使用全局变量的方法之一是为你的应用只创建一个唯一的全局变量
还有就是应用闭包隐藏信息,从而减少全局污染
var MYAPP = {};
MYAPP.stooge = {
"first-name": "Joe",
"last-name": "Howard"
};
MYAPP.flight = {
airline: "Oceanic",
number: 815,
departure: {
IATA: "SYD",
time: "2014-09--22 14:45",
city: "Sydney"
},
arrival: {
IATA: "LAX",
time: "2004-09-23",
city: "Los angeles"
}
};
...
函数
JavaScript 中的函数就是对象。
对象的字面量产生的对象连接到Object.prototype
函数对象连接到Function.prototype(该原型对象本身连接到Object.prototype)
函数创建时拥有两个隐藏属性
函数对象的prototype 属性的值拥有constructor 属性且值位该函数的对象
//创建一个名为add的变量,并用来吧两个数字相加的函数赋值给它
var add = function (a, b) {
return a+b;
};
包括四个部分
1. 保留字 function
2. 函数名 它可以省略 ,省略时 即为 匿名函数(anonymous)
3. 圆括号内的 一组参数
4. 花括号的一组语句 函数的主体
函数也可以被定义再其他函数中,一个内部函数处理可以访问自己的参数和变量,同时也能访问把它嵌套再其中的父函数的参数和变量。通过函数字面量创建的函数对象包含一个链接到外部上下文的链接。这称为闭包(closure).
调用一个函数会暂停当前函数的执行,传递控制权和参数给新函数。
除了声明时定义的参数外,每个函数还会接收两个附加的参数 : this 和 arguments。
一共四种调用模式
(初始化关键参数 this 的不同)
1. 方法调用模式
2. 函数调用模式
3. 构造器调用模式
4.apply调用模式
方法调用模式
当一个函数被保存为对象的一个属性时,我们称它位一个方法。
当一个方法被调用时,this 被绑定到该对象。
如果调用表达式包含一个提取属性的动作(即包含一个. 表达式或者 [subscrript]下标表达式),那么它就是被当做一个方法来调用。
// 创建一个myObject 对象 它有一个value 属性和一个increment 方法
// increment 方法接收一个可选的参数。 如果参数不是数字, 那么默认使用数字1
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'undefined' ? inc : 1;
}
};
myObject.increment();
document.writlen(myObject.value); // 1
myObject.increment(2);
document.writlen(myObject.value); // 3
函数调用
当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用
this 绑定到全局对象。这是一个语言设计上的错误。
解决方法:
myObject.double = function () {
var that = this;
var helper = function () {
that.value = add(that.value, that.value);
}
helper();// 以函数的形式调用helper
};
// 以方法的形式调用 double
myObject.double();
document.writeln(myObject.value);
构造器调用模式
JavaScript 是一门基于原型继承的语言。
一个函数前面带上new 来调用,那么背地里将会创建一个连接到该函数的prototype成员的新对象 ,this 绑定到那个新对象上。
// 创建一个名为 Quo 的构造器函数。
var Quo = function (String) {
this.status = String;
};
// Quo 的所有实例提供一个名为get_status 的公共方法
Quo.prototype.get_status = function () {
return this.satus;
};
// 构造一个Quo 实例。
var myQuo = new Quo("confused");
document.writeln(myQue.get_status()): // "confused"
Apply 调用模式
因为JavaScript 是一门函数式的面向对象编程语言,所以函数可以拥有方法
apply 方法让我们构建一个参数数组传递给调用函数。它允许我们选择this的值。
apply 接收连个参数,一个是要绑定this的值, 第二个是一个参数数组
// 构造一个包含两个数字的数组,并把他们两个相加
var array = [3, 4];
var sum = add.apply(null, array); // sum 值为7
// 构造包含status 成员的对象
var statusObject = {
status: 'A-OK'
};
// statusObject 并没有继承自Que.prototype,但我们可以再statusObject 上
// 调用get_status 方法,尽管statusObject 并没有一个名为get_status 方法
var status = Quo.prototype.get_status.apply(statusObject);
// status 的值为 'A-OK'
函数调用时会默认有一个 arguments 参数,arguments 可以访问被调用时传递给它的参数列表。
var sum = function () {
var i,sum = 0;
for ( i = 0; i < arguments.length; i += 1) {
sum += arguments[i];
}
return sum;
};
document.writeln(sum(4, 8, 15, 16, 23, 42)); // 108
return 语句 用来提前返回
异常是干扰程序的正常流程的不寻常的事故(但并非出乎意料)
var add = function (a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw {
name: 'TypeError',
message: 'add needs numbers'
};
return a + b;
}
}
throw 语句中断函数的执行,它应该抛出一个exception对象
该exception 对象将被传递到一个try 语句的catch 从句:
// 构造一个try_it 函数 以不正确的方式调用之前的add 函数
var try_it = function () {
try {
add("seven");
} catch (e){
document.writeln(e.name + ': ' + e.message);
}
}
JavaScript 允许给语言的基本类型扩充功能。
扩充 Function.prototype 加入method方法,方便添加方法
Funtction.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
}
比如给Number.prototype 增加一个integer方法
Number.method('integer', function () {
return Math[this < 0 ? 'ceil' : 'floor' ] (this);
})
document.writeln((-10 / 3).integer()); // -3
不存在时再添加。
Funtction.prototype.method = function (name, func) {
if (! this.prototype[name]) {
this.prototype[name] = func;
}
return this;
}
递归函数就是会直接或间接的调用自身的一种函数。
汉诺塔
var hanoi = function (disc, src, aux, dst) {
if (disc > 0) {
hanoi(disc - 1, src, dst, aux);
document.writeln('Move disc ' + disc + ' from ' + src + 'to ' + dst);
hanoi(disc - 1, aux, src, dst);
}
}
作用域控制着变量与参数的可见性以及生命周期
作用域的好处是内部函数可以访问定义他们的外部函数的的参数和变量(除了this和arguments)
函数可以访问它被创建时所处的上下文环境,所以才叫闭包
保护value不被非法更改
var myObject = (function () {
var value = 0;
return {
increment: function (inc) {
value += typeof inc === 'number' ? inc : 1;
},
getValue: function () {
return value;
}
};
}());
定义一个函数,它设置一个DOM节点为黄色,然后把它渐变为白色
var fade = function (node) {
var level = 1;
var step = function () {
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
if (level < 15) {
level += 1;
setTimeout(step, 100);
}
};
setTimeout(step, 100);
};
fade(document.body);
避免再循环中创建函数,他可能只会带来无谓的基三,还会引起混淆。
我们可以现在循环外创建一个辅助函数,让这个负主函数再返回绑定了当前i的函数
var add_the_handlers =function (nodes) {
var helper = function (i) {
return function (e) {
alert(i);
};
};
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = helper(i);
}
};
正常模拟服务器响应
request = prepare_the_request();
response = send_request_synchronously(request);
display(response );
这是最自然的写法,但是如果网络上的同步请求会导致客户端进入假死状态。
所以应用异步函数,防止阻塞
request = prepare_the_request();
response = send_request_synchronously(request, function (response){
display(request);
});
我们可以使用函数和闭包来构造模块
给String 增加一个deentityify 方法, 理想方式是把它放入闭包:
String.method('deentityify', function () {
// 字符实体表 银蛇字符实体名字到对应的字符
var entity = {
quot: '"',
lt: '<',
gt: '>'
};
return function () {
// 这是真正的deenityify 方法。 调用replace
return this.replace(/&([^&;]+);/g,
function(a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
);
};
}());
一些方法返回this而不是undefined 就可以启用级联。
在一个级联中,我们可以单独一条语句一次调用同一个对象的很多方法。
Ajax 类库调用例子:
getElement('myBoxDiv')
.move(350, 150)
.width(100)
.height(100);
函数也是值,从而我们可以用有趣的方式去操作函数值。柯里化允许我们把函数传递给它的参数相结合,产生一个新的函数。
var add1 = add.curry(1);
document.writeln(add1(6)); // 7
创建curry需要注意
arguments 数组并非一个真正的数组,所以并没有concat方法。要避开这个问题,我们必须要再两个argumengts数组上都应用slice方法
Function.method('curry', function () {
var slice = Array.prototype.slice,
args = slice.appley(arguments),
that = this;
return function () {
return that.apply(null,args.concat(slice.apply(arguments)));
}
});
函数可以将先前的操作的结果记录在某个对象里,从而避免无谓的重复运算。
以Fibonacci 为列
var fibonacci = function (n) {
return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2 );
}
for (var i = 0; i <= 10; i += 1) {
document.writeln('//' + i + ': ' + fibonacci(i));
}
上面fibonacci函数被调用了453次,我们调用了 11次。它调用了吱声442次去计算。
改进加入记忆:
var fibonacci = function () {
var memo = [0,1];
var fib = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = fib(n - 1) + fib (n - 2);
memo[n] = result;
}
return result;
};
return fib;
}();
fibonacci函数只被调用了29次,我们调用了 11次。它调用了18次去取得之前的存储结果。
推广记忆功能
var memoizer = function (memo, formula) {
var recur = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = formula (recur, n);
memo[n] = result;
}
return result;
};
return recur;
};
那么fibonacci 函数就可以这样写
var fibonacci = memoizer([0,1], function (recur, n) {
return recur (n -1) + recur (n -2);
});
带记忆功能的阶乘函数
var factorial = memoizer ([1,1], function (recur, n) {
return n*recur(n - 1);
});