jav写游戏外挂怎么写的

&img src=&/50/v2-abd76f164ace864b713abfe_b.jpg& data-rawwidth=&1175& data-rawheight=&619& class=&origin_image zh-lightbox-thumb& width=&1175& data-original=&/50/v2-abd76f164ace864b713abfe_r.jpg&&&blockquote&&p&原文链接:&a href=&/?target=https%3A///blog/the-many-faces-of-functions-in-javascript& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&The Many Faces of Functions in JavaScript&i class=&icon-external&&&/i&&/a&&/p&&p&作者:&a href=&/?target=https%3A///about/bocouper/leo-balter& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Leo Balter&i class=&icon-external&&&/i&&/a& and &a href=&/?target=https%3A///about/bocouper/rick-waldron& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Rick Waldron&i class=&icon-external&&&/i&&/a&&/p&&p&译者:&a href=&/?target=https%3A//Liubara.github.io& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Liubara&i class=&icon-external&&&/i&&/a&&/p&&p&本文已获得作者授权,转载请注明出处。&/p&&/blockquote&&p&如果你曾与JavaScript代码打过交道,你应该会很熟悉如何定义和调用函数,但是你真的知道有多少种定义函数的方法吗?对于编写和维护测试&a href=&/?target=https%3A///tc39/test262& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Test262&i class=&icon-external&&&/i&&/a&(浏览器JavaScript标准测试)来说,这是一个十分常见的挑战,尤其是当一个新特性出现且与现有的函数语法有关联,或者扩展了现有函数的API时。有必要确保新的或被提议的语法和API是有效的,且对语言中的每一个现有变体都是有效的。&/p&&p&本文内容是对JavaScript中已经存在的函数语法格式的概述说明。本文档不包括类声明和表达式,因为这些形式生成的对象不是“可调用的”,对于本文来说,我们只关注生成“可调用的”函数对象的格式。此外,这篇文章也不包括参数列表(包括默认参数、解构,或者尾逗号),因为这些话题足够再写一篇文章了。&/p&&p&&i&译者注:类声明、默认参数、解构和尾逗号是ES6/7的新特性~~不妨碍看这篇文章哈&/i&&/p&&h2&旧方法&/h2&&h2&函数声明和表达式&/h2&&p&大家都知道,最广泛应用也是最早的函数定义形式就是函数声明和函数表达式。前者是最初设计的一部分(1995)并出现在&a href=&/?target=https%3A//www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262%2C%25201st%2520edition%2C%2520June%.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&第一个版本的规范(1997年)&i class=&icon-external&&&/i&&/a&中,后者是在&a href=&/?target=https%3A//www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262%2C%25203rd%2520edition%2C%2520December%.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&第三个版本(1999年)&i class=&icon-external&&&/i&&/a&引入的。我们可以从这些规范中提取三种不同的定义形式:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// 函数声明&/span&
&span class=&kd&&function&/span& &span class=&nx&&BindingIdentifier&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&c1&&// 命名函数表达式&/span&
&span class=&c1&&// (BindingIdentifier对函数外部不可访问)&/span&
&span class=&p&&(&/span&&span class=&kd&&function&/span& &span class=&nx&&BindingIdentifier&/span&&span class=&p&&()&/span& &span class=&p&&{});&/span&
&span class=&c1&&// 匿名函数表达式&/span&
&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{});&/span&
&/code&&/pre&&/div&&p&要注意的是匿名函数表达式可能仍然有一个“名称”,Mike Pennisi在这篇文章&a href=&/?target=https%3A///blog/whats-in-a-function-name& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&What's in a Function Name?&i class=&icon-external&&&/i&&/a&中解释得很清楚。&/p&&h2&&b&Function&/b&构造器&/h2&&p&当我们在讨论一种语言的“函数API”的时候,就已经开始讨论&b&Function&/b&构造器。在考虑最初的语言设计时,函数声明的语法形式可以被解释为函数构造器的API的“文字”形式。&b&Function&/b&构造器为定义函数提供了一种方法:通过N个字符串参数指定函数参数和函数主体,(如下面的例子)最后一个字符串参数始终是函数主体(需要指出的是,这是一种动态求值形式,会有潜在的安全风险)。对于大多数情况来说,这种形式并不适合,因此它的使用非常稀少——但是自从第一个版本的ECMAScript以来,它就一直存在在JavaScript中了。&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&k&&new&/span& &span class=&nb&&Function&/span&&span class=&p&&(&/span&&span class=&s1&&'x'&/span&&span class=&p&&,&/span& &span class=&s1&&'y'&/span&&span class=&p&&,&/span& &span class=&s1&&'return x *'&/span&&span class=&p&&);&/span&
&/code&&/pre&&/div&&h2&新方法&/h2&&p&自从&a href=&/?target=https%3A//www.ecma-international.org/ecma-262/6.0/index.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ES2015&i class=&icon-external&&&/i&&/a&推出以来,已经引入了几种新的语法形式。这些形式的变化是巨大的!&/p&&h2&not-so-anonymous函数声明&/h2&&p&这是一种新的匿名函数声明形式,如果你曾用过ES Modules,应该清楚这种语法。虽然它可能看起来与匿名函数表达式非常相似,&a href=&/?target=https%3A//tc39.github.io/ecma262/%23sec-function-definitions-static-semantics-boundnames& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&但它实际上有一个默认名称,即“default”&i class=&icon-external&&&/i&&/a&&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// not-so-anonymous 函数声明&/span&
&span class=&kr&&export&/span& &span class=&k&&default&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&/code&&/pre&&/div&&p&顺便说一下,这个“名称”(指“default”)本身并不是有效的标识符,并且没有绑定在该匿名函数上。&/p&&h2&方法定义&/h2&&p&对于下面这个例子,大家应该能很快发现它定义了匿名和命名函数表达式作为属性的值。注意,这些不是不同的语法形式。它们是之前讨论过的函数表达式的示例,是在初始对象时编写的。这种形式最初是在ES3中引入的。&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kd&&let&/span& &span class=&nx&&object&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&propertyName&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{},&/span&
&span class=&p&&};&/span&
&span class=&kd&&let&/span& &span class=&nx&&object&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&c1&&// (BindingIdentifier在这个函数中不可访问)&/span&
&span class=&nx&&propertyName&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span& &span class=&nx&&BindingIdentifier&/span&&span class=&p&&()&/span& &span class=&p&&{},&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&p&在ES5中引入了访问器属性定义:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kd&&let&/span& &span class=&nx&&object&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&get&/span& &span class=&nx&&propertyName&/span&&span class=&p&&()&/span& &span class=&p&&{},&/span&
&span class=&nx&&set&/span& &span class=&nx&&propertyName&/span&&span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{},&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&p&从ES2015开始,JavaScript提供了一个简单的语法来定义方法,这种语法包括文字属性名称和计算属性名称形式,以及访问器形式:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kd&&let&/span& &span class=&nx&&object&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&propertyName&/span&&span class=&p&&()&/span& &span class=&p&&{},&/span&
&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{},&/span&
&span class=&nx&&get&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&]()&/span& &span class=&p&&{},&/span&
&span class=&nx&&set&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&](&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{},&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&p&我们还可以使用这些新形式作为类声明和表达式中的原型方法的定义:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// 类声明&/span&
&span class=&kr&&class&/span& &span class=&nx&&C&/span& &span class=&p&&{&/span&
&span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&nx&&get&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&nx&&set&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&](&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 类表达式&/span&
&span class=&kd&&let&/span& &span class=&nx&&C&/span& &span class=&o&&=&/span& &span class=&kr&&class&/span& &span class=&p&&{&/span&
&span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&nx&&get&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&nx&&set&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&](&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{}&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&p&和定义静态方法:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// 类声明&/span&
&span class=&kr&&class&/span& &span class=&nx&&C&/span& &span class=&p&&{&/span&
&span class=&kr&&static&/span& &span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&nx&&get&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&nx&&set&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&](&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 类表达式&/span&
&span class=&kd&&let&/span& &span class=&nx&&C&/span& &span class=&o&&=&/span& &span class=&kr&&class&/span& &span class=&p&&{&/span&
&span class=&kr&&static&/span& &span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&nx&&get&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&nx&&set&/span& &span class=&p&&[&/span&&span class=&s2&&&computedAccessorName&&/span&&span class=&p&&](&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{}&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&h2&箭头函数&/h2&&p&作为ES2015最具争议性的函数之一,箭头函数已经变得众所周知且无处不在。箭头函数语法是这样定义的,它为函数声明提供了两种不同的格式:赋值表达式(箭头后面没有跟“{”大括号时为赋值表达式)和函数体(代码中包括0到多个语句时为函数体)。这个语法还允许在描述单个参数时不加圆括号,然而0个或一个以上参数需要加圆括号。这些语法结构允许箭头函数拥有多种书写形式:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// 木有参数的赋值表达式&/span&
&span class=&p&&(()&/span& &span class=&o&&=&&/span& &span class=&mi&&2&/span& &span class=&o&&**&/span& &span class=&mi&&2&/span&&span class=&p&&);&/span&
&span class=&c1&&// 单个参数,忽略括号的赋值表达式&/span&
&span class=&p&&(&/span&&span class=&nx&&x&/span& &span class=&o&&=&&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&mi&&2&/span&&span class=&p&&);&/span&
&span class=&c1&&// 单个参数,忽略括号且直接跟函数体&/span&
&span class=&p&&(&/span&&span class=&nx&&x&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span& &span class=&k&&return&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&mi&&2&/span&&span class=&p&&;&/span& &span class=&p&&});&/span&
&span class=&c1&&// 括起来的参数列表和赋值表达式&/span&
&span class=&p&&((&/span&&span class=&nx&&x&/span&&span class=&p&&,&/span& &span class=&nx&&y&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&nx&&y&/span&&span class=&p&&);&/span&
&/code&&/pre&&/div&&p&在上面所示的最后一种形式中,参数被描述为一个&b&括起来的参数列表&/b&,因为它们被包装在括号内。这提供了一种语法来标记参数列表或特殊的解构模式,就像&b&({ x })= & x&/b&。&/p&&p&未被括起来的形式——也就是没有圆括号的形式——即在箭头函数只能表现为只用一个标识符名称作为参数的形式。当箭头函数在异步函数或生成器中定义时,这个标识符名称需要以&b&await&/b&或&b&yeild&/b&作为前缀定义。但这是我们在箭头函数中能得到的最大程度的不用括号括起来参数列表的情况。&/p&&p&箭头函数可以(也经常)出现在初始化值或属性定义的赋值中,上面所示的箭头函数表达式已经包括了这种情况,如下面的例子所示:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kd&&let&/span& &span class=&nx&&foo&/span& &span class=&o&&=&/span& &span class=&nx&&x&/span& &span class=&o&&=&&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&mi&&2&/span&&span class=&p&&;&/span&
&span class=&kd&&let&/span& &span class=&nx&&object&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&propertyName&/span&&span class=&o&&:&/span& &span class=&nx&&x&/span& &span class=&o&&=&&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&mi&&2&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&h2&生成器&/h2&&p&生成器有一种特殊的语法,除了箭头函数和定义setter / getter方法的时候不能添加之外,可以被添加在其他所有语法形式中。我们可以用其生成函数声明、表达式、定义,甚至构造函数。让我们把它们列出来:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// 生成器声明&/span&
&span class=&kd&&function&/span& &span class=&o&&*&/span&&span class=&nx&&BindingIdentifer&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&c1&&// 另一种 not-so-anonymous 生成器声明&/span&
&span class=&kr&&export&/span& &span class=&k&&default&/span& &span class=&kd&&function&/span& &span class=&o&&*&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&c1&&// 命名生成器表达式&/span&
&span class=&c1&&// (BindingIdentifier 对函数外部不可访问)&/span&
&span class=&p&&(&/span&&span class=&kd&&function&/span& &span class=&o&&*&/span&&span class=&nx&&BindingIdentifier&/span&&span class=&p&&()&/span& &span class=&p&&{});&/span&
&span class=&c1&&// 匿名生成器表达式&/span&
&span class=&p&&(&/span&&span class=&kd&&function&/span& &span class=&o&&*&/span&&span class=&p&&()&/span& &span class=&p&&{});&/span&
&span class=&c1&&// 方法定义&/span&
&span class=&kd&&let&/span& &span class=&nx&&object&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&o&&*&/span&&span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{},&/span&
&span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{},&/span&
&span class=&p&&};&/span&
&span class=&c1&&// 类声明中的方法定义&/span&
&span class=&kr&&class&/span& &span class=&nx&&C&/span& &span class=&p&&{&/span&
&span class=&o&&*&/span&&span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 类声明中的静态方法定义&/span&
&span class=&kr&&class&/span& &span class=&nx&&C&/span& &span class=&p&&{&/span&
&span class=&kr&&static&/span& &span class=&o&&*&/span&&span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 类表达式中的方法定义&/span&
&span class=&kd&&let&/span& &span class=&nx&&C&/span& &span class=&o&&=&/span& &span class=&kr&&class&/span& &span class=&p&&{&/span&
&span class=&o&&*&/span&&span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&};&/span&
&span class=&c1&&// 类表达式中的静态方法定义&/span&
&span class=&kd&&let&/span& &span class=&nx&&C&/span& &span class=&o&&=&/span& &span class=&kr&&class&/span& &span class=&p&&{&/span&
&span class=&kr&&static&/span& &span class=&o&&*&/span&&span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&h2&ES2017&/h2&&h2&异步函数&/h2&&p&经历了几年的发展,异步函数将于2017年6月发布ES2017的EcmaScript语言规范的第8版引入。尽管如此,许多开发人员已经使用了这个特性,这要归功于&a href=&/?target=https%3A//babeljs.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Babel&i class=&icon-external&&&/i&&/a&的早期实现支持!&/p&&p&Async函数语法为描述异步操作提供了一种干净而统一的方式。调用时,Async函数对象将返回一个Promise对象,这个对象将在异步函数返回时被解析。当包含一个&b&await&/b&表达式时,异步函数可能暂停函数的执行,然后将其用作异步函数的返回值。&/p&&p&它的语法和我们从其他形式中所知道的一样:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// 异步函数声明&/span&
&span class=&nx&&async&/span& &span class=&kd&&function&/span& &span class=&nx&&BindingIdentifier&/span&&span class=&p&&()&/span& &span class=&p&&{&/span& &span class=&cm&&/**/&/span& &span class=&p&&}&/span&
&span class=&c1&&// not-so-anonymous 异步函数声明&/span&
&span class=&kr&&export&/span& &span class=&k&&default&/span& &span class=&nx&&async&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span& &span class=&cm&&/**/&/span& &span class=&p&&}&/span&
&span class=&c1&&// 命名异步函数表达式&/span&
&span class=&c1&&// (BindingIdentifier is not accessible outside of this function)&/span&
&span class=&p&&(&/span&&span class=&nx&&async&/span& &span class=&kd&&function&/span& &span class=&nx&&BindingIdentifier&/span&&span class=&p&&()&/span& &span class=&p&&{});&/span&
&span class=&c1&&// 匿名异步函数表达式&/span&
&span class=&p&&(&/span&&span class=&nx&&async&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{});&/span&
&span class=&c1&&// 异步方法&/span&
&span class=&kd&&let&/span& &span class=&nx&&object&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&async&/span& &span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{},&/span&
&span class=&nx&&async&/span& &span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{},&/span&
&span class=&p&&};&/span&
&span class=&c1&&// 类声明中的异步方法&/span&
&span class=&kr&&class&/span& &span class=&nx&&C&/span& &span class=&p&&{&/span&
&span class=&nx&&async&/span& &span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&nx&&async&/span& &span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 类声明中的静态异步方法&/span&
&span class=&kr&&class&/span& &span class=&nx&&C&/span& &span class=&p&&{&/span&
&span class=&kr&&static&/span& &span class=&nx&&async&/span& &span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&nx&&async&/span& &span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 类声明中的异步方法&/span&
&span class=&kd&&let&/span& &span class=&nx&&C&/span& &span class=&o&&=&/span& &span class=&kr&&class&/span& &span class=&p&&{&/span&
&span class=&nx&&async&/span& &span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&nx&&async&/span& &span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&};&/span&
&span class=&c1&&// 类表达式中的异步方法&/span&
&span class=&kd&&let&/span& &span class=&nx&&C&/span& &span class=&o&&=&/span& &span class=&kr&&class&/span& &span class=&p&&{&/span&
&span class=&kr&&static&/span& &span class=&nx&&async&/span& &span class=&nx&&methodName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&nx&&async&/span& &span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&h2&异步箭头函数&/h2&&p&&b&async&/b&和&b&await&/b&并不局限于普通的声明和表达式形式,它们也可以用于箭头函数:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// 单个参数的赋值表达式&/span&
&span class=&p&&(&/span&&span class=&nx&&async&/span& &span class=&nx&&x&/span& &span class=&o&&=&&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&mi&&2&/span&&span class=&p&&);&/span&
&span class=&c1&&// 单个参数的函数体&/span&
&span class=&p&&(&/span&&span class=&nx&&async&/span& &span class=&nx&&x&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span& &span class=&k&&return&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&mi&&2&/span&&span class=&p&&;&/span& &span class=&p&&});&/span&
&span class=&c1&&// 括起来的参数列表后跟赋值表达式&/span&
&span class=&p&&(&/span&&span class=&nx&&async&/span& &span class=&p&&(&/span&&span class=&nx&&x&/span&&span class=&p&&,&/span& &span class=&nx&&y&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&nx&&y&/span&&span class=&p&&);&/span&
&span class=&c1&&// 括起来的参数列表后跟函数体&/span&
&span class=&p&&(&/span&&span class=&nx&&async&/span& &span class=&p&&(&/span&&span class=&nx&&x&/span&&span class=&p&&,&/span& &span class=&nx&&y&/span&&span class=&p&&)&/span& &span class=&o&&=&&/span& &span class=&p&&{&/span& &span class=&k&&return&/span& &span class=&nx&&x&/span& &span class=&o&&**&/span& &span class=&nx&&y&/span&&span class=&p&&;&/span& &span class=&p&&});&/span&
&/code&&/pre&&/div&&h2&继续更新的ES2017&/h2&&h2&异步生成器 &b&Async Generators&/b&&/h2&&p&在接下来的ES017中,&b&async&/b&和&b&await&/b&关键字将被扩展以支持新的异步生成器形式。这个特性的进展可以通过&a href=&/?target=https%3A///tc39/proposal-async-iteration& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&proposal’s github repository&i class=&icon-external&&&/i&&/a&进行跟踪。您可能已经猜到,这是&b&async&/b&、&b&await&/b&和现有的生成器声明和生成器表达式语法的组合。调用时,异步生成器返回一个迭代器,它的&b&next()&/b&方法返回Promise对象然后用迭代器对象解析,而不是直接返回迭代器对象。&/p&&p&可以在许多地方发现异步生成器,你可能已经生成器函数中见到它了。&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&c1&&// 异步生成器声明&/span&
&span class=&nx&&async&/span& &span class=&kd&&function&/span& &span class=&o&&*&/span&&span class=&nx&&BindingIdentifier&/span&&span class=&p&&()&/span& &span class=&p&&{&/span& &span class=&cm&&/**/&/span& &span class=&p&&}&/span&
&span class=&c1&&// not-so-anonymous 异步生成器声明&/span&
&span class=&kr&&export&/span& &span class=&k&&default&/span& &span class=&nx&&async&/span& &span class=&kd&&function&/span& &span class=&o&&*&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&c1&&// 异步生成器表达式&/span&
&span class=&c1&&// (BindingIdentifier在函数外部不可访问)&/span&
&span class=&p&&(&/span&&span class=&nx&&async&/span& &span class=&kd&&function&/span& &span class=&o&&*&/span&&span class=&nx&&BindingIdentifier&/span&&span class=&p&&()&/span& &span class=&p&&{});&/span&
&span class=&c1&&// 匿名函数表达式&/span&
&span class=&p&&(&/span&&span class=&nx&&async&/span& &span class=&kd&&function&/span& &span class=&o&&*&/span&&span class=&p&&()&/span& &span class=&p&&{});&/span&
&span class=&c1&&// 方法定义&/span&
&span class=&kd&&let&/span& &span class=&nx&&object&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&nx&&propertyName&/span&&span class=&p&&()&/span& &span class=&p&&{},&/span&
&span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{},&/span&
&span class=&p&&};&/span&
&span class=&c1&&// 类声明中的原型方法定义&/span&
&span class=&kr&&class&/span& &span class=&nx&&C&/span& &span class=&p&&{&/span&
&span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&nx&&propertyName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 类表达式中的原型方法定义&/span&
&span class=&kd&&let&/span& &span class=&nx&&C&/span& &span class=&o&&=&/span& &span class=&kr&&class&/span& &span class=&p&&{&/span&
&span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&nx&&propertyName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&};&/span&
&span class=&c1&&// 类声明中的静态方法定义&/span&
&span class=&kr&&class&/span& &span class=&nx&&C&/span& &span class=&p&&{&/span&
&span class=&kr&&static&/span& &span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&nx&&propertyName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 类表达式中的静态方法定义&/span&
&span class=&kd&&let&/span& &span class=&nx&&C&/span& &span class=&o&&=&/span& &span class=&kr&&class&/span& &span class=&p&&{&/span&
&span class=&kr&&static&/span& &span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&nx&&propertyName&/span&&span class=&p&&()&/span& &span class=&p&&{}&/span&
&span class=&kr&&static&/span& &span class=&nx&&async&/span& &span class=&o&&*&/span&&span class=&p&&[&/span&&span class=&s2&&&computedName&&/span&&span class=&p&&]()&/span& &span class=&p&&{}&/span&
&span class=&p&&};&/span&
&/code&&/pre&&/div&&h2&复杂的挑战&/h2&&p&每个函数语法格式不仅对学习和使用是挑战,而且对JS运行时间和Test262的实现和维护也是一个挑战。当引入新的语法形式时,Test262必须与所有相关的语法规则一起测试该新形式。例如,将默认参数语法的测试形式限制在简单的函数声明形式中,并假设在其他格式下该语法也正常起作用是不明智的。每一个语法规则都必须经过测试,将这些测试任务分配给一个人是不合理的。所以导致了测试生成工具的设计和实现。测试生成工具提供了一种确保能够覆盖(函数格式的多少)更详尽的方法。&/p&&p&这个项目现在包含了一系列由不同的测试用例和模板组成的&a href=&/?target=https%3A///tc39/test262/tree/master/src& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&源文件&i class=&icon-external&&&/i&&/a&,例如,&a href=&/?target=https%3A///tc39/test262/tree/master/src/arguments& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&如何检查每个函数格式的参数&i class=&icon-external&&&/i&&/a&,或者&a href=&/?target=https%3A///tc39/test262/tree/master/src/function-forms& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&函数格式测试&i class=&icon-external&&&/i&&/a&,甚至更多超出范围的函数形式,在这些函数形式中,&a href=&/?target=https%3A///tc39/test262/tree/master/src/dstr-binding& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&解构绑定&i class=&icon-external&&&/i&&/a&和&a href=&/?target=https%3A///tc39/test262/tree/master/src/dstr-assignment& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&解构赋值&i class=&icon-external&&&/i&&/a&都是适用的。&/p&&p&尽管它可能导致密集的和长时间的发送请求,但是覆盖率总是会提高,而且可能&a href=&/?target=https%3A///tc39/test262/pull/651& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&总是会发现新的错误&i class=&icon-external&&&/i&&/a&。&/p&&h2&为什么了解所有的函数格式是很重要的?&/h2&&p&如果不需要在Test262上编写测试,计算和列出所有函数表单可能并不重要。这里已经列出了&a href=&/?target=https%3A///tc39/test262/tree/master/src/function-forms/default& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&许多格式的模板&i class=&icon-external&&&/i&&/a&。新的测试可以很容易地使用现有的模板作为起点。&/p&&p&确保EcmaScript规范的良好测试是Test262的主要任务。这对所有的JavaScript运行时间都有直接的影响,我们识别的格式越多,覆盖率就越全面,这将帮助新功能更无缝地集成,不管您使用的平台是什么。&/p&
原文链接:作者: and 译者:本文已获得作者授权,转载请注明出处。如果你曾与JavaScript代码打过交道,你应该会很熟悉如何定义和调用函数,但是你真的知道有多少种定义函数的方法…
&img src=&/50/v2-2fbdb42027_b.jpg& data-rawwidth=&540& data-rawheight=&486& class=&origin_image zh-lightbox-thumb& width=&540& data-original=&/50/v2-2fbdb42027_r.jpg&&&p&在介绍事件委托之前,我们先介绍另外两个知识点:其他的&b&事件绑定方法&/b&和&b&事件冒泡及捕获&/b&。&/p&&h2&事件绑定/监听的方法&/h2&&p&&b&1.直接绑定&/b&&/p&&p&顾名思义,直接在DOM元素上绑定onclick、onmouseover、onmouseout、onmousedown、onmouseup、ondblclick、onkeydown、onkeypress、onkeyup等事件&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var ul = document.getElementById('ul')
console.log(ul)
ul.onclick = function() {
console.log('click 事件绑定成功')
&/code&&/pre&&/div&&p&这种方法最简单,也是DOM level0最早支持的一种方法。但是这个方法存在一个很大的问题。那就是如果一个元素绑定事件时,有可能覆盖掉前面已经绑定好的事件!尤其是存在多个js文件时。为了解决这个问题,level2新增了&b&事件监听&/b&&/p&&p&&b&2.事件监听&/b&&/p&&p&事件监听实现的功能和直接绑定差不多,但是新增了一个特点。那就是无论监听次,都不会覆盖掉前面的监听事件。本质原因是&b&监听事件每次都会生产一个全新的匿名函数,和前面的函数完全不同,自然不会覆盖。&/b&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var ul = document.getElementById('ul')
ul.addEventListener('click', function() {
console.log('事件绑定成功1')
ul.addEventListener('click', function() {
console.log('事件绑定成功2')
&/code&&/pre&&/div&&h2&&b&事件冒泡和捕获&/b&&/h2&&img src=&/50/v2-2fbdb42027_b.jpg& data-rawwidth=&540& data-rawheight=&486& class=&origin_image zh-lightbox-thumb& width=&540& data-original=&/50/v2-2fbdb42027_r.jpg&&&br&&blockquote&事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。&/blockquote&&br&&blockquote&事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。&/blockquote&&br&&blockquote&事件冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。&/blockquote&&br&&p&如果想阻止事件冒泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。&/p&&br&&p&当然,我们也能通过改变addEventListener的第三个参数改变事件的执行顺序。(false为冒泡阶段执行,true为捕获阶段执行,默认为false)&/p&&br&&h2&&b&事件委托&/b&&/h2&&p&我们实现事件委托就是基于上面几个理论。&/p&&p&现在,我们先设想一种情况,ul标签中的每个li标签都要绑定一个点击事件&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&ul id=&ul&&
&li id=&l1&&1&/li&
&li id=&l2&&2&/li&
&li id=&l3&&3&/li&
&li id=&l4&&4&/li&
&/code&&/pre&&/div&&p&如果回调函数不一样,那当然没什么好说,只能一个个写不同的函数。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&l1.addEventListener('click', function() {})
l2.addEventListener('click', function() {})
l3.addEventListener('click', function() {})
l4.addEventListener('click', function() {})
&/code&&/pre&&/div&&p&但如果执行的函数都一样,且li个数很多,这就显得非常麻烦了。尤其是li个数不确定的时候(JavaScript动态生成),这种方式更是不适用。&/p&&p&聪明的你可能已经想到,我点击了li,这个时候不也等于点击了ul吗?那我直接把点击事件绑定在ul上不就好了?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var ul = document.getElementById('ul')
ul.addEventListener('click', function() {})
&/code&&/pre&&/div&&p&我们点击li,确实是实现了我们想要的功能。但又有一个新的问题。那就是ul如果有padding可能就会出bug。我们点击li之外,ul之内时,事件也触发了!这种结果肯定不是我们想要的。&/p&&img src=&/50/v2-7f63bf3cb86b88f108d0_b.png& data-rawwidth=&909& data-rawheight=&231& class=&origin_image zh-lightbox-thumb& width=&909& data-original=&/50/v2-7f63bf3cb86b88f108d0_r.png&&&br&&p&那我触发事件之前先判断一下点击的目标不就好了?如果点的是li就触发,不然就不触发。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&ul.addEventListener('click', function(e) {
// 检查事件源e.targe是否为Li
if (e.target && e.target.nodeName.toUpperCase == &LI&) {
// 真正的处理过程在这里
console.log(&点击成功&);
&/code&&/pre&&/div&&p&再次测试函数,发现基本没有问题了。点击li触发事件,li之外不触发事件。**这也是大多数人实现事件委托的方法,但却不是一个好的实现方法**。因为这种方法也存在着明显的bug&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&ul id=&ul&&
&li id=&l1&&&span&1&/span&&/li&
&li id=&l2&&2&/li&
&li id=&l3&&3&/li&
&li id=&l4&&4&/li&
&/code&&/pre&&/div&&p&如果第一个li外面套了一个span的情况呢?我们再次点击第一个li,它!不!触!发!事!件!我们console.log出这个时候点击的标签,发现是span,自然不触发事件。显然我们之前写的事件委托有bug。有可能我们点击li的时候,li还有后代元素。这时候我们就应该先判断点击的元素的祖先元素当中有没有li,如果有li,那点击的还是li,如果没有,那就是真的没有了。最后写出的事件委托函数如下&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&ul.addEventListener('click', function() {
let el = e.target
while (el && !el.matches(selector)) {
el = el.parentNode
if (element === el) {
console.log('执行回调函数')
&/code&&/pre&&/div&&h2&&b&总结&/b&&/h2&&p&搜索出的解决方法有时候也存在着不足之处,并非完美,我们还是需要多实践来检验正确与否。尽信书不如无书。&/p&
在介绍事件委托之前,我们先介绍另外两个知识点:其他的事件绑定方法和事件冒泡及捕获。事件绑定/监听的方法1.直接绑定顾名思义,直接在DOM元素上绑定onclick、onmouseover、onmouseout、onmousedown、onmouseup、ondblclick、onkeydown、onkeypress、onkey…
&img src=&/50/v2-e660a4be3bd66d5336bb_b.jpg& data-rawwidth=&1728& data-rawheight=&1080& class=&origin_image zh-lightbox-thumb& width=&1728& data-original=&/50/v2-e660a4be3bd66d5336bb_r.jpg&&在 zepto 源码中,$.fn 对象 有个 ready 函数,其中有这样一句 setTimeout(fn,0);&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&nx&&$&/span&&span class=&p&&.&/span&&span class=&nx&&fn&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&
&span class=&nx&&ready&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&callback&/span&&span class=&p&&){&/span&
&span class=&c1&&// don't use &interactive& on IE &= 10 (it can fired premature)&/span&
&span class=&c1&&//&/span&
&span class=&c1&&// document.readyState:当document文档正在加载时,返回&loading&。当文档结束渲染但在加载内嵌资源时,返回&interactive&,并引发DOMContentLoaded事件。当文档加载完成时,返回&complete&,并引发load事件。&/span&
&span class=&c1&&// document.documentElement.doScroll:IE有个特有的方法doScroll可以检测DOM是否加载完成。 当页面未加载完成时,该方法会报错,直到doScroll不再报错时,就代表DOM加载完成了&/span&
&span class=&c1&&//&/span&
&span class=&c1&&// 关于 setTimeout(fn ,0) 的作用 可以参考文章:/silin6/p/4333999.html&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&readyState&/span& &span class=&o&&===&/span& &span class=&s2&&&complete&&/span& &span class=&o&&||&/span&
&span class=&p&&(&/span&&span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&readyState&/span& &span class=&o&&!==&/span& &span class=&s2&&&loading&&/span& &span class=&o&&&&&/span& &span class=&o&&!&/span&&span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&documentElement&/span&&span class=&p&&.&/span&&span class=&nx&&doScroll&/span&&span class=&p&&))&/span&
&span class=&nx&&setTimeout&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&(){&/span& &span class=&nx&&callback&/span&&span class=&p&&(&/span&&span class=&nx&&$&/span&&span class=&p&&)&/span& &span class=&p&&},&/span& &span class=&mi&&0&/span&&span class=&p&&)&/span&
&span class=&k&&else&/span& &span class=&p&&{&/span&
&span class=&c1&&// 监听移除事件&/span&
&span class=&kd&&var&/span& &span class=&nx&&handler&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&removeEventListener&/span&&span class=&p&&(&/span&&span class=&s2&&&DOMContentLoaded&&/span&&span class=&p&&,&/span& &span class=&nx&&handler&/span&&span class=&p&&,&/span& &span class=&kc&&false&/span&&span class=&p&&)&/span&
&span class=&nb&&window&/span&&span class=&p&&.&/span&&span class=&nx&&removeEventListener&/span&&span class=&p&&(&/span&&span class=&s2&&&load&&/span&&span class=&p&&,&/span& &span class=&nx&&handler&/span&&span class=&p&&,&/span& &span class=&kc&&false&/span&&span class=&p&&)&/span&
&span class=&nx&&callback&/span&&span class=&p&&(&/span&&span class=&nx&&$&/span&&span class=&p&&)&/span&
&span class=&p&&}&/span&
&span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&addEventListener&/span&&span class=&p&&(&/span&&span class=&s2&&&DOMContentLoaded&&/span&&span class=&p&&,&/span& &span class=&nx&&handler&/span&&span class=&p&&,&/span& &span class=&kc&&false&/span&&span class=&p&&)&/span&
&span class=&nb&&window&/span&&span class=&p&&.&/span&&span class=&nx&&addEventListener&/span&&span class=&p&&(&/span&&span class=&s2&&&load&&/span&&span class=&p&&,&/span& &span class=&nx&&handler&/span&&span class=&p&&,&/span& &span class=&kc&&false&/span&&span class=&p&&)&/span&
&span class=&p&&}&/span&
&span class=&k&&return&/span& &span class=&k&&this&/span&
&span class=&p&&},&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&时间设为 0 ,就是要立即执行,那为什么还要特意将 fn 套到 setTimeout 里面呢?&br&&/p&&br&&br&一、线程&br&1、浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器通常由以下常驻线程组成:GUI 渲染线程,javascript 引擎线程,浏览器事件触发线程,定时触发器线程,异步 http 请求线程。&br&&br&&ul&&li&&strong&GUI 渲染线程&/strong&:负责渲染浏览器界面 HTML 元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。在 Javascript 引擎运行脚本期间, GUI 渲染线程都是处于挂起状态的,也就是说被”冻结”。即 GUI 渲染线程与 JS 引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。&/li&&br&&li&&strong&javascript 引擎线程&/strong&:也可以称为 JS 内核,主要负责处理 Javascript 脚本程序,例如 V8 引擎。Javascript 引擎线程理所当然是负责解析 Javascript 脚本,运行代码。浏览器无论什么时候都只有一个 JS 线程在运行 JS 程序。&/li&&br&&li&&strong&浏览器事件触发线程&/strong&:当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JS 引擎的处理。这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX 异步请求等,但由于JS的单线程关系所有这些事件都得排队等待 JS 引擎处理。&/li&&br&&li&&strong&定时触发器线程&/strong&:浏览器定时计数器并不是由 JavaScript 引擎计数的, 因为 javaScript 引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确, 因此通过单独线程来计时并触发定时是更为合理的方案。&/li&&br&&li&&strong&异步 http 请求线程&/strong&:在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 JavaScript 引擎的处理队列中等待处理。&/li&&/ul&&br&举个例子,看看这些线程如何配合工作的:&br&&p&例子1:异步请求是由线程 JavaScript 执行线程、HTTP 请求线程 和 事件触发线程 共同完成的。JavaScript 执行线程 执行异步请求代码,这时浏览器会开一条新的 HTTP 请求线程 来执行请求,JavaScript 执行线程则继续执行 执行队列 中剩下的其他任务。然后在未来的某一时刻 事件触发线程 监视到之前的发起的 HTTP 请求已完成,它就会把完成事件的回调代码插入到 JavaScript 执行队列尾部 等待 JavaScript 执行线程空闲时来处理。&/p&&p&例子2:定时触发(setTimeout 和 setInterval)是由浏览器的 定时器线程 执行的定时计数,然后在定时时间结束时把定时处理函数的执行代码插入到 JavaScript 执行队列的尾端(所以用这两个函数的时候,实际的执行时间是大于或等于指定时间的,不保证能准确定时的)。&/p&&p&2、javascript 是单线程的,同一个时间只能做一件事。&/p&&p&这里说一下 js调用栈(call stack),可以从根本上理解单线程的执行过程。&br&推荐一个神器网站:&a href=&/?target=http%3A///loupe/%3Fcode%3DJC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7%21%21%21PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%253D& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Loupe&i class=&icon-external&&&/i&&/a& 可以用来图形化调用栈的过程,大家可以把例子在网站上运行一下,好用到疯掉。&/p&&p&js 调用栈(call stack):函数被调用时,就会被加入到调用栈顶部,执行结束之后,就会从调用栈顶部移除该函数,这种数据结构的关键在于后进先出,即 LIFO(last-in,first-out)。&/p&&br&举个例子:&br&来自(&a href=&/?target=https%3A//developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&并发模型与Event Loop&i class=&icon-external&&&/i&&/a&) &br&&img src=&/v2-e71380cfad85bcdacb66124_b.png& data-rawwidth=&294& data-rawheight=&270& class=&content_image& width=&294&&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function f(b) {
var a = 12;
return a + b + 35;
function g(x) {
var m = 4;
return f(m * x);
&/code&&/pre&&/div&调用 g 函数 的时候,创建了第一个 堆( Heap ) 栈(stack) 帧 ,包含了 g 的参数和局部变量。当 g 调用 f 的时候,第二个 堆栈帧 就被创建、并置于第一个 堆栈帧 之上,包含了 f 的参数和局部变量。当 f 返回时,最上层的 堆栈帧 就出栈了(剩下 g 函数调用的 堆栈帧 )。当 g 返回的时候,栈就空了。&br&&br&再举个例子:&br&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kd&&function&/span& &span class=&nx&&test&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&setTimeout&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&alert&/span&&span class=&p&&(&/span&&span class=&mi&&1&/span&&span class=&p&&)&/span&
&span class=&p&&},&/span&&span class=&mi&&1000&/span&&span class=&p&&);&/span&
&span class=&nx&&alert&/span&&span class=&p&&(&/span&&span class=&mi&&2&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&nx&&test&/span&&span class=&p&&();&/span&
&/code&&/pre&&/div&在执行函数 test 的时候,test 先入栈,如果不给 alert(1)加 setTimeout,那么 alert(1)第 2 个入栈,最后是 alert(2)。但现在给 alert(1)加上 setTimeout 后,alert(1)就被加入到了一个新的堆栈中等待,并1s后执行,因此实际的执行结果就是先 alert(2),再 alert(1)。&br&&br&&p&3、任务队列(消息队列):&/p&&ul&&li&函数分为两种:同步和异步。&/li&&/ul&&p&    同步函数:如果在函数A返回的时候,调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果),那么这个函数就是同步的。&/p&&p&
例子:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&console.log('Hi’);
//函数返回时,就看到了预期的效果:在控制台打印了一个字符串
&/code&&/pre&&/div&&p&
异步函数即如果在函数A返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的。&br&    例子:&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&setTimeout(fn, 1000);//setTimeout是异步过程的发起函数,fn是回调函数。
&/code&&/pre&&/div&&br&&ul&&li&任务也分为两种:同步任务和异步任务。&/li&&/ul&&p&    同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。&br&    异步任务:主线程发起一个异步请求(即执行异步函数),相应的工作线程(浏览器事件触发线程、异步http请求线程等)接收请求并告知主线程已收到(异步函数返回);主线程可以继续执行后面的代码,同时工作线程执行异步任务;工作线程完成工作后,将完成消息放到任务(消息)队列,主线程通过事件循环过程去取任务(消息),然后执行一定的动作(调用回调函数)。&/p&&p&    图中主线程即 Stack,任务队列即 Queue。&/p&&img src=&/v2-e71380cfad85bcdacb66124_b.png& data-rawwidth=&294& data-rawheight=&270& class=&content_image& width=&294&&&ul&&li&任务队列:任务(消息)队列是一个先进先出的队列,它里面存放着各种任务(消息)。&/li&&li&事件循环(event loop):事件循环是指主线程重复从任务(消息)队列中取任务(消息)、执行的过程。取一个任务(消息)并执行的过程叫做一次循环。&/li&&/ul&&p&    事件循环中有事件两个字的原因:任务(消息)队列中的每条消息实际上都对应着一个事件——dom事件。&br&    例子:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kd&&var&/span& &span class=&nx&&button&/span& &span class=&o&&=&/span& &span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&getElement&/span&&span class=&p&&(&/span&&span class=&s1&&'#btn'&/span&&span class=&p&&);&/span&
&span class=&nx&&button&/span&&span class=&p&&.&/span&&span class=&nx&&addEventListener&/span&&span class=&p&&(&/span&&span class=&s1&&'click'&/span&&span class=&p&&,&/span&&span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&e&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&();&/span&
&span class=&p&&})&/span&&span class=&err&&;&/span&
&/code&&/pre&&/div&&p&从异步过程的角度看,addEventListener 函数就是异步过程的发起函数,事件监听器函数就是异步过程的回调函数。事件触发时,表示异步任务完成,会将事件监听器函数封装成一条消息放到消息队列中,等待主线程执行。&/p&&p&&br&那么 任务(消息)到底是什么呢? 任务(消息)就是注册异步任务时添加的回调函数。如果 一个异步函数没有回调,那么他就不会放到任务(消息)队列里。&/p&&p&&br&总结一下过程:主线程在执行完当前循环中的所有代码后,就会到任务(消息)队列取出一条消息,并执行它。到此为止,就完成了工作线程对主线程的通知,回调函数也就得到了执行。如果一开始主线程就没有提供回调函数,工作线程就没必要通知主线程,从而也没必要往消息队列放消息。&/p&&p&例子: 工作线程为异步 http 请求线程即 Ajax 线程&/p&&img src=&/v2-27ddecaa86d15bd739ec_b.png& data-rawwidth=&649& data-rawheight=&361& class=&origin_image zh-lightbox-thumb& width=&649& data-original=&/v2-27ddecaa86d15bd739ec_r.png&&&p&最后注意异步过程的回调函数,一定不在当前这一轮事件循环中执行。而是当 这一轮执行完了,主线程空了,再从任务(消息)队列中取。&/p&&br&&p&再来看一下这张图&/p&&img src=&/v2-ad11c6a2a9ec099e0973_b.png& data-rawwidth=&734& data-rawheight=&689& class=&origin_image zh-lightbox-thumb& width=&734& data-original=&/v2-ad11c6a2a9ec099e0973_r.png&&&p&主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在&任务队列&中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取&任务队列&,依次执行那些事件所对应的回调函数。&/p&&p&&br&三、setTimeout(fn, 0) 的作用&/p&&p&调用 setTimeout 函数会在一个时间段过去后在队列中添加一个消息。这个时间段作为函数的第二个参数被传入。如果队列中没有其它消息,消息会被马上处理。但是,如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表示最少的时间,而非确切的时间。&/p&&p&零延迟 (Zero delay) 并不是意味着回调会立即执行。在零延迟调用 setTimeout 时,其并不是过了给定的时间间隔后就马上执行回调函数。其等待的时间基于队列里正在等待的消息数量。也就是说,setTimeout()只是将事件插入了任务队列,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证回调函数一定会在setTimeout()指定的时间执行。&/p&&p&例子&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span& &span class=&nx&&setTimeout&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&mi&&1&/span&&span class=&p&&);&/span&
&span class=&p&&},&/span&&span class=&mi&&0&/span&&span class=&p&&);&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&mi&&2&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&执行结果2,1。因为只有在执行完第二行以后,主线程空了,才会去任务队列中取任务执行回调函数。&/p&&br&&p&总结:setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在&任务队列&的尾部添加一个事件,因此要等到主线程把同步任务和&任务队列&现有的事件都处理完,才会得到执行。&br&在某种程度上,我们可以利用setTimeout(fn,0)的特性,修正浏览器的任务顺序。&/p&&br&&img src=&/v2-adb595a978c24adcefda95_b.png& data-rawwidth=&200& data-rawheight=&200& class=&content_image& width=&200&&&br&学习并感谢&br&&a href=&/?target=https%3A///a/2358& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript:彻底理解同步、异步和事件循环(Event Loop)&i class=&icon-external&&&/i&&/a&&br&&a href=&/?target=https%3A//developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&并发模型与Event Loop&i class=&icon-external&&&/i&&/a&&br&&a href=&/?target=http%3A///silin6/p/4333999.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript下的setTimeout(fn,0)意味着什么? - linkFly - 博客园&i class=&icon-external&&&/i&&/a&&br&&a href=&/?target=http%3A///blog/2014/10/event-loop.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 运行机制详解:再谈Event Loop&i class=&icon-external&&&/i&&/a&&br&&br&最后再推一遍 神器,一定要用哦 &a href=&/?target=http%3A///loupe/%3Fcode%3DJC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7%21%21%21PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%253D& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Loupe&i class=&icon-external&&&/i&&/a&
在 zepto 源码中,$.fn 对象 有个 ready 函数,其中有这样一句 setTimeout(fn,0);$.fn = {
ready: function(callback){
// don't use "interactive" on IE &= 10 (it can fired premature)
// document.readyState:当document文档正在加载时,返回"l…
&p&这个问题问的很好。&/p&&p&流行的MVVM的JS库/框架都有共同的特点就是数据绑定,在数据变更后响应式的自动进行相关计算并变更DOM展现。所以这个问题也可以理解为&strong&如何实现MVVM库/框架的数据绑定&/strong&。&/p&&p&常见的数据绑定的实现有脏值检测,基于ES5的getter和setter,以及ES已被废弃的Object.observe,和ES6中添加的Proxy。&/p&&br&&b&&u&脏值检测&/u&&/b&&p&angular使用的就是脏值检测,原理是比较新值和旧值,当值真的发生改变时再去更改DOM,所以angular中有一个$digest。那么为什么在像ng-click这样的内置指令在触发后会自动变更呢?原理也很简单,在ng-click这样的内置指令中最后追加了$digest。&/p&&p&简易的实现一个脏值检测:&br&&/p&&br&&div class=&highlight&&&pre&&code class=&language-html&&&span class=&cp&&&!DOCTYPE html&&/span&
&span class=&nt&&&html&&/span&
&span class=&nt&&&head&&/span&
&span class=&nt&&&meta&/span& &span class=&na&&charset=&/span&&span class=&s&&&utf-8&&/span& &span class=&nt&&/&&/span&
&span class=&nt&&&title&&/span&two-way binding&span class=&nt&&&/title&&/span&
&span class=&nt&&&/head&&/span&
&span class=&nt&&&body&/span& &span class=&na&&onload=&/span&&span class=&s&&&init()&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&button&/span& &span class=&na&&ng-click=&/span&&span class=&s&&&inc&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&/button&&/span&
&span class=&nt&&&button&/span& &span class=&na&&ng-click=&/span&&span class=&s&&&reset&&/span&&span class=&nt&&&&/span&
&span class=&nt&&&/button&&/span&
&span class=&nt&&&span&/span& &span class=&na&&style=&/span&&span class=&s&&&color:red&&/span& &span class=&na&&ng-bind=&/span&&span class=&s&&&counter&&/span&&span class=&nt&&&&/span&&/span&
&span class=&nt&&&span&/span& &span class=&na&&style=&/span&&span class=&s&&&color:blue&&/span& &span class=&na&&ng-bind=&/span&&span class=&s&&&counter&&/span&&span class=&nt&&&&/span&&/span&
&span class=&nt&&&span&/span& &span class=&na&&style=&/span&&span class=&s&&&color:green&&/span& &span class=&na&&ng-bind=&/span&&span class=&s&&&counter&&/span&&span class=&nt&&&&/span&&/span&
&span class=&nt&&&script &/span&&span class=&na&&type=&/span&&span class=&s&&&text/javascript&&/span&&span class=&nt&&&&/span&
&span class=&cm&&/* 数据模型区开始 */&/span&
&span class=&kd&&var&/span& &span class=&nx&&counter&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span&
&span class=&kd&&function&/span& &span class=&nx&&inc&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&counter&/span&&span class=&o&&++&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&kd&&function&/span& &span class=&nx&&reset&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&counter&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&cm&&/* 数据模型区结束 */&/span&
&span class=&cm&&/* 绑定关系区开始 */&/span&
&span class=&kd&&function&/span& &span class=&nx&&init&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&bind&/span&&span class=&p&&();&/span&
&span class=&p&&}&/span&
&span class=&kd&&function&/span& &span class=&nx&&bind&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&kd&&var&/span& &span class=&nx&&list&/span& &span class=&o&&=&/span& &span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&querySelectorAll&/span&&span class=&p&&(&/span&&span class=&s2&&&[ng-click]&&/span&&span class=&p&&);&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kd&&var&/span& &span class=&nx&&i&/span&&span class=&o&&=&/span&&span class=&mi&&0&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span&&span class=&o&&&&/span&&span class=&nx&&list&/span&&span class=&p&&.&/span&&span class=&nx&&length&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&list&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&].&/span&&span class=&nx&&onclick&/span& &span class=&o&&=&/span& &span class=&p&&(&/span&&span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&index&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&return&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nb&&window&/span&&span class=&p&&[&/span&&span class=&nx&&list&/span&&span class=&p&&[&/span&&span class=&nx&&index&/span&&span class=&p&&].&/span&&span class=&nx&&getAttribute&/span&&span class=&p&&(&/span&&span class=&s2&&&ng-click&&/span&&span class=&p&&)]();&/span&
&span class=&nx&&apply&/span&&span class=&p&&();&/span&
&span class=&p&&};&/span&
&span class=&p&&})(&/span&&span class=&nx&&i&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&kd&&function&/span& &span class=&nx&&apply&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&kd&&var&/span& &span class=&nx&&list&/span& &span class=&o&&=&/span& &span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&querySelectorAll&/span&&span class=&p&&(&/span&&span class=&s2&&&[ng-bind='counter']&&/span&&span class=&p&&);&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kd&&var&/span& &span class=&nx&&i&/span&&span class=&o&&=&/span&&span class=&mi&&0&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span&&span class=&o&&&&/span&&span class=&nx&&list&/span&&span class=&p&&.&/span&&span class=&nx&&length&/span&&span class=&p&&;&/span& &span class=&nx&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&nx&&list&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&].&/span&&span class=&nx&&innerHTML&/span& &span class=&o&&!=&/span& &span class=&nx&&counter&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&list&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&].&/span&&span class=&nx&&innerHTML&/span& &span class=&o&&=&/span& &span class=&nx&&counter&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&cm&&/* 绑定关系区结束 */&/span&
&span class=&nt&&&/script&&/span&
&span class=&nt&&&/body&&/span&
&span class=&nt&&&/html&&/span&
&/code&&/pre&&/div&&br&&p&这样做的坏处是自己变更数据后,是无法自动改变DOM的,必须要想办法触发apply(),所以只能借助ng-click的包装,在ng-click中包含真实的click事件监听并追加脏值检测以判断是否要更新DOM。&/p&&br&&p&另外一个坏处是如果不注意,每次脏值检测会检测大量的数据,而很多数据是没有检测的必要的,容易影响性能。&/p&&br&&p&关于如何实现一个和angular一样的脏值检测,知道原理后还有很多工作要去做,以及如何优化等等。如果有兴趣可以看看民工叔曾经推荐的《Build Your Own Angular.js》,第一章Scope便讲了如何实现angular的作用域和脏值检测。对了,上面的例子也是从民工叔的博客稍加修改来的,建议最后去看下原文,链接在参考资料中。&/p&&br&&b&&u&ES5的getter与setter&/u&&/b&&p&在ES5中新增了一个Object.defineProperty,直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&nb&&Object&/span&&span class=&p&&.&/span&&span class=&nx&&defineProperty&/span&&span class=&p&&(&/span&&span class=&nx&&obj&/span&&span class=&p&&,&/span& &span class=&nx&&prop&/span&&span class=&p&&,&/span& &span class=&nx&&descriptor&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&其接受的第三个参数可以取get和set并各自对应一个getter和setter方法:&br&&/p&&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kd&&var&/span& &span class=&nx&&a&/span& &span class=&o&&=&/span& &span class=&p&&{&/span& &span class=&nx&&zhihu&/span&&span class=&o&&:&/span&&span class=&mi&&0&/span& &span class=&p&&};&/span&
&span class=&nb&&Object&/span&&span class=&p&&.&/span&&span class=&nx&&defineProperty&/span&&span class=&p&&(&/span&&span class=&nx&&a&/span&&span class=&p&&,&/span& &span class=&s1&&'zhihu'&/span&&span class=&p&&,&/span& &span class=&p&&{&/span&
&span class=&nx&&get&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&s1&&'get:'&/span& &span class=&o&&+&/span& &span class=&nx&&zhihu&/span&&span class=&p&&);&/span&
&span class=&k&&return&/span& &span class=&nx&&zhihu&/span&&span class=&p&&;&/span&
&span class=&p&&},&/span&
&span class=&nx&&set&/span&&span class=&o&&:&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&value&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&zhihu&/span& &span class=&o&&=&/span& &span class=&nx&&value&/span&&span class=&p&&;&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&s1&&'set:'&/span& &span class=&o&&+&/span& &span class=&nx&&zhihu&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&p&&});&/span&
&span class=&nx&&a&/span&&span class=&p&&.&/span&&span class=&nx&&zhihu&/span& &span class=&o&&=&/span& &span class=&mi&&2&/span&&span class=&p&&;&/span& &span class=&c1&&// set:2&/span&
&span class=&nx&&console&/span&&span class=&p&&.&/span&&span class=&nx&&log&/span&&span class=&p&&(&/span&&span class=&nx&&a&/span&&span class=&p&&.&/span&&span class=&nx&&zhihu&/span&&span class=&p&&);&/span& &span class=&c1&&// get:2&/span&
&span class=&c1&&// 2&/span&
&/code&&/pre&&/div&&br&&p&基于ES5的getter和setter可以说几乎完美符合了要求。为什么要说&b&几乎&/b&呢?&/p&&br&&p&首先IE8及更低版本IE是无法使用的,而且这个特性是没有polyfill的,无法在不支持的平台实现,这也是基于ES5getter和setter的Vue.js不支持IE8及更低版本IE的原因。也许有人会提到avalon,avalon在低版本IE借助vbscript一些黑魔法实现了类似的功能。&/p&&p&除此之外,还有一个问题就是修改数组的length,直接用索引设置元素如items[0] = {},以及数组的push等变异方法是无法触发setter的。&br&如果想要解决这个问题可以参考Vue的做法,在Vue的observer/array.js中,Vue直接修改了数组的原型方法:&/p&&br&&div class=&highlight&&&pre&&code class=&language-js&&&span class=&kr&&const&/span& &span class=&nx&&arrayProto&/span& &span class=&o&&=&/span& &span class=&nb&&Array&/span&&span class=&p&&.&/span&&span class=&nx&&prototype&/span&
&span class=&kr&&export&/span& &span class=&kr&&const&/span& &span class=&nx&&arrayMethods&/span& &span class=&o&&=&/span& &span class=&nb&&Object&/span&&span class=&p&&.&/span&&span class=&nx&&create&/span&&span class=&p&&(&/span&&span class=&nx&&arrayProto&/span&&span class=&p&&)&/span&
&span class=&cm&&/**&/span&
&span class=&cm&& * Intercept mutating methods and emit events&/span&
&span class=&cm&& */&/span&
&span class=&p&&;[&/span&
&span class=&s1&&'push'&/span&&span class=&p&&,&/span&
&span class=&s1&&'pop'&/span&&span class=&p&&,&/span&
&span class=&s1&&'shift'&/span&&span class=&p&&,&/span&
&span class=&s1&&'unshift'&/span&&span class=&p&&,&/span&
&span class=&s1&&'splice'&/span&&span class=&p&&,&/span&
&span class=&s1&&'sort'&/span&&span class=&p&&,&/span&
&span class=&s1&&'reverse'&/span&
&span class=&p&&]&/span&
&span class=&p&&.&/span&&span class=&nx&&forEach&/span&&span class=&p&&(&/span&&span class=&kd&&function&/span& &span class=&p&&(&/span&&span class=&nx&&method&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&c1&&// cache original method&/span&
&span class=&kd&&var&/span& &span class=&nx&&original&/span& &span class=&o&&=&/span& &span class=&nx&&arrayProto&/span&&span class=&p&&[&/span&&span class=&nx&&method&/span&&span class=&p&&]&/span&
&span class=&nx&&def&/span&&span class=&p&&(&/span&&span class=&nx&&arrayMethods&/span&&span class=&p&&,&/span& &span class=&nx&&method&/span&&span class=&p&&,&/span& &span class=&kd&&function&/span& &span class=&nx&&mutator&/span& &span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&c1&&// avoid leaking arguments:&/span&
&span class=&c1&&// /closure-with-arguments&/span&
&span class=&kd&&var&/span& &span class=&nx&&i&/span& &span class=&o&&=&/span& &span class=&nx&&arguments&/span&&span class=&p&&.&/span&&span class=&nx&&length&/span&
&span class=&kd&&var&/span& &span class=&nx&&args&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&nb&&Array&/span&&span class=&p&&(&/span&&span class=&nx&&i&/span&&span class=&p&&)&/span&
&span class=&k&&while&/span& &span class=&p&&(&/span&&span class=&nx&&i&/span&&span class=&o&&--&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&nx&&args&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&nx&&arguments&/span&&span class=&p&&[&/span&&span class=&nx&&i&/span&&span class=&p&&]&/span&
&span class=&p&&}&/span&
&span class=&kd&&var&/span& &span class=&nx&&result&/span& &span class=&o&&=&/span& &span class=&nx&&original&/span&&span class=&p&&.&/span&&span class=&nx&&apply&/span&&span class=&p&&(&/span&&span class=&k&&this&/span&&span class=&p&&,&/span& &span class=&nx&&args&/span&&span class=&p&&)&/span&
&span class=&kd&&var&/span& &span class=&nx&&ob&/span& &span class=&o&&=&/span& &span class=&k&&this&/span&&span class=&p&&.&/span&&span class=&nx&&__ob__&/span&
&span class=&kd&&var&/span& &span class=&nx&&inserted&/span&
&span class=&k&&switch&/span& &span class=&p&&(&/span&&span class=&nx&&method&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&k&&case&/span& &span class=&s1&&'push'&/span&&span class=&o&&:&/span&
&span class=&nx&&inserted&/s

我要回帖

更多关于 你的线上 jav zoom 的文章

 

随机推荐