JavaScript简要

注意


  • 声明提前(hoisting)
var scope = "global";
function f() {
    console.log(scope);        // -> undefined, 该作用域中scope已定义,但在这个地方还未赋值
    var scope = "local";
    console.log(scope);        // -> local
}
  • null和undefined不能包含属性
  • with语句
o = {"x" : 0};
with(o) x = 1;        // o.x = 1
with(o) y = 2;        // 定义了全局变量y
// 即with语句提供访问对象属性的简便方法,但并不创建对象属性
  • 函数的call方法
f = (function (x) {
    return this.a + x;
})
(function () {
    this.a = 20;
    f.call(this, 30);      // 50
    f.apply(this, [22])    // 42
} ())

JavaScript - The Good Parts

对象


JavaScript类型有数字,字符串,布尔(true/false), null和undefined. 所有其它值均为对象。

Object Literals

  • 空对象: {}
  • 非空对象为大括号扩住的键值对: {name : value, ...}
  • 值value部分可以是Object Literal
  • 键name部分可以是任意字符串, 若name是一个合法的JavaScript标志符且不是保留字,则引号可以省略
  • 原型为Object.prototype

取值

  • obj["name"]
  • obj.name

更新值

  • obj["name"] = value
  • obj.name = value

引用

  • 对象通过引用传递

原型

每个对象关联到一个原型对象,并且它可以从该对象继承属性. 所有创建自Object Literal的对象均关联到Object.prototype.

当创建一个对象时,你可以选择作为它原型的对象.

// create方法接收一个对象,并以改对象为原型创建一个新的对象
if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

原型关系只在取值操作中起作用. 当试图获取对象的某个不存在的属性,JavaScript转而从该对象的原型获取该属性,若仍不存在,继续从原型的原型获取,以此类推,若一直到Object.prototype都没有该属性,则返回undefined

反射

typeof "hello"            // 'string'
typeof 1                  // 'number'
typeof {}                 // 'object'
typeof undefined          // 'undefined'
typeof ''.toString        // 'function'

typeof 会沿着原型链查找属性,若不希望这样可以使用hasOwnProperty属性

枚举

for (var in aobject) {...}会枚举对象aobject的所有属性,包括函数和原型属性,且对属性的枚举顺序不定。当然也可以通过将想要枚举的属性放在列表中使用for而不是for in进行枚举:

var i;
var properties = [
    'first-name',
    'middle-name',
    'last-name',
    'profession'
];
for (i = 0; i < properties.length; i += 1) {
    document.writeln(properties[i] + ': ' + another_stooge[properties[i]]);
}

删除

delete操作符用于删除对象的属性,它不会干涉原型

减少全局变量的使用

减少全局变量的使用可以通过创建一个单一的全局变量作为你应用的容器:

var MyApp = {};
MyApp.var1 = ...;
MyApp.var2 = ...;

还可以使用闭包减少全局变量的使用

函数


函数对象

  • JavaScript中的函数是对象
  • 原型为Function.prototype,Function.prototype的原型为Object.prototype

Function Literals

函数通过function literal创建

var add = function(a, b) {
    return a+b;
};

调用

在调用函数时,除了声明的形参之外,还接收了两个参数: thisargumentsthis的值取决于调用模式,在JavaScript中有四种调用模式:

  1. 方法调用模式(method invocation pattern)

当一个函数作为一个属性存储在对象中,称其为方法,当方法被调用时,this绑定到该对象上

 var myObject = {
         value: 0,
         increment: function (inc) {
             this.value += typeof inc === 'number' ? inc : 1;
} };
myObject.increment( ); 
document.writeln(myObject.value);             // 1 
myObject.increment(2);
document.writeln(myObject.value);             // 3

方法可以通过this访问对象的属性, this和对象的绑定时在方法调用时完成的

  1. 函数调用模式(function invocation pattern)

若函数不是对象的属性,对其的调用为函数调用。这种模式的调用,this被绑定到全局对象上(global object),这种设计使得嵌套函数中内部函数调用时不能直接使用this获取调用它的函数的上下文,当然这可通过简单的方法解决

myObject.double = function () {
    var that = this;              // Workaround.
    var helper = function () {
        that.value = add(that.value, that.value);
    };
    helper();                     // Invoke helper as a function. 
};
myObject.double( );                    // Invoke double as a method.
document.writeln(myObject.getValue()); // 6
  1. 构造函数调用模式(constructor invocation pattern)

如果一个函数通过new前缀调用,一个新的对象被创建,该对象隐式关联到该函数的原型成员,this被绑定到该新建的对象

var Quo = function(string) {
    this.status = string;
};
Quo.prototype.get_status = function() {
    return this.status;
};
var myQuo = new Quo("Confused");
document.writeln(myQuo.get_status());        // "Confused"

通过前缀new调用的函数被称为构造函数(constructor)

  1. apply调用模式(apply invocation pattern)

JavaScript中函数可以有方法,函数的apply方法可以用来进行函数调用,它接收两个参数,第一个时this要绑定的对象,第二个是调用函数所需的参数列表

var add = function(a, b) {return a+b;};
var array = [1,2];
var sum = add.apply(null, array);            // sum = 3
var statusObject = {"status" : "OK"};
Quo.prototype.get_status.apply(statusObject)             // "OK"
(function () {return this.status;}).apply(statusObject)  // "OK"

arguments

函数的隐式参数arguments里存放了所有的调用参数,它不是数组,只是一个类似列表的对象,它有length属性,但缺少数组的其它属性

返回

函数可以通过return语句返回一个值,若是通过构造函数模式调用(new),没有通过return返回对象,则this被返回

异常

var add = function (a, b) {
         if (typeof a !== 'number' || typeof b !== 'number') {
             throw {
                 name: 'TypeError',
                 message: 'add needs numbers'
            }; 
        }
return a + b; 
}
var try_it = function () { try {
             add("seven");
         } catch (e) {
             document.writeln(e.name + ': ' + e.message);
         }
}
try_it( );

throw语句应该接收一个异常对象,它至少包含两个属性,name表明异常类型和一个描述异常的message属性。异常对象可以被catch语句捕捉

作用域

var foo = function() {
    var a = 3, b = 5;            // 此时a=3, b=5
    var bar = function() {
        var b = 7, c = 11;       // 此时a=3, b=7, c=11
        a += b + c;              // 此时a=21, b=7, c=11
    };
    bar();                       // 此时a=21, b=5
}

在函数内部定义的变量,函数外部不可见,对函数内的所有区块都可见。

闭包

对于JavaScript的作用域,其好的部分是,一个函数的内部定义的函数能够获取该函数的除thisarguments的所有参数和变量的值。
一种极为有趣的情形是内部函数可以有比它外部函数更长的生命周期:

var myObject = function () {
    var value = 0;
    return {
        "increment": function(inc) {
            value += typeof inc === "number" ? inc : 1;
        },
        "getValue": function() {
            return value;
        }
    };
}();

Callbacks

// 异步处理请求
request = prepare_the_request();   // 准备请求
send_request_asynchronously(request, function(response) {
        deal_with(response);
    });                            // 异步发送请求,将处理请求响应的回调函数作为参数传入

模块

一个模块是提供接口但是隐藏状态和实现的函数或者对象。可以通过函数闭包构造模块。通过函数构造模块可以几乎完全消除全局变量,因而可以规避JavaScript最糟糕的特性。

例如我们要给字符串增加一个方法deentityify,它用于翻译HTML实体到对应的字符。最直观的实现方式就是将实体和对应的字符存放在一个对象中,但是在什么地方存放这个对象呢?放在全局变量中?呵...,放在函数中?每次调用函数时解释器该对象要重新求值。理想的方式是放在闭包中,甚至可以添加一个方法用于添加新的HTML实体

// 给Function.prototype添加一个新的方法method,该方法接收两个参数
// 字符串name和函数func。于是所有的函数都增加了这样一个方法(因为所有
// 函数的原型链的底端都是Function.prototype)
// 当一个函数调用这个method方法时,该函数为自己的原型添加了一个
// 名为name的方法(参考上面的方法调用模式)
Function.prototype.method = function (name, func) {
        this.prototype[name] = func;
        return this;
};
// 为String的原型添加deentityify方法
String.method("deentityify", function(){
    var entity = {
        quot: '"',
        lt: '<',
        gt: '>'
    };
    return function () {
       return this.replace(/&([^&;]+);/g,
           function (a, b) {
               var r = entity[b];
               return typeof r === 'string' ? r : a;
          });
    };
}());
'<">'.deentityify()    // -> <">

该实现中entity变量仅deentityfy方法可以访问

模块的一般模式----将一个函数作为模块,在该模块函数中可以定义一些私有变量和函数,另有一些特权函数,这些函数作为模块函数的返回值或者被存放在外部可见的地方,这些特权函数可以通过闭包访问模块函数中的私有变量和函数

这种模式可以消除全局变量,还可以用于生成安全的对象

var serial_maker = function () {
    var prefix = '',
    var seq    = 0;
    return {
        set_prefix: function (p) {
            prefix = String(p);
        },
        set_seq: function (s) {
            seq = s; },
            gensym: function () {
            var result = prefix + seq; seq += 1;
            return result;
        }
    };
};
var seqer = serial_maker(); 
seqer.set_prefix = ('Q';) 
seqer.set_seq = (1000);
var unique = seqer.gensym();

如果将上面的seqer.gensym提供给第三方使用,该函数可以生成唯一的标识符但是前缀和序列号不可更改。

级联(cascade)

如果一个方法没有返回值,我们可以令其返回this(即返回调用该方法的对象自己),这样我们就开启了级联模式。

Curry

JavaScript中可以这样实现curry

Function.method("curry", function() {
    // 之前提到过arguments不是数组,所以这里需要将它转换为数组(为了使用concat方法)
    var slice = Array.prototype.slice,
          args = slice.apply(arguments), 
          that = this;
    return function() {
        return that.apply(null, args.concat(slice.apply(arguments)));
    };
});

Memorization

考虑递归求解Fibonacci数列

var fibonacci = function (n) {
     return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};

这种求解方法非常低效,太多的工作被重做,记住已做的工作:

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;
}();

这种方式可以一般化成memorizer,自行实现吧
func = memorizer(init_values, func)

继承


创建函数对象时,生成函数对象的构造函数执行类似下面的操作:

this.prototype = {constructor: this};

生成的函数对象被赋予了一个属性prototype,其(属性prototype)值为一个对象,该对象有值为生成的函数对象的属性constructor。

函数的构造函数模式调用(new前缀调用)若以函数的方法实现的话,可以这样实现:

Function.method("new", function() {
    # 创建一个以构造函数原型为原型的对象 
    var that = Object.create(this.prototype);
    # 调用构造构造函数,绑定构造函数的this到新建对象that
    var other = this.apply(that, arguments);
    # 构造函数返回的是对象返回该对象,否则返回新建对象that
    return (typeof other === 'object' && other) || that;
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,697评论 2 17
  • 单例模式 适用场景:可能会在场景中使用到对象,但只有一个实例,加载时并不主动创建,需要时才创建 最常见的单例模式,...
    Obeing阅读 2,045评论 1 10
  • 官方中文版原文链接 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大...
    HetfieldJoe阅读 2,980评论 4 14
  • 昨天在群里又看到我一直奉为经典的话:我们还是学生。 几年前,我常面试新同事,遇到很多以“我还是个学生”为标的的道德...
    三摩地阅读 611评论 0 0
  • 下午和一位许久没有联系的朋友聊了会,因为工作忙碌,大家同在一座城市,也很少有聚会。 今天,看她发了一个...
    野蛮超体阅读 334评论 2 1