前端学习总结(三)javascript——前端世界的精髓所在

一 说一说js的前世,今生与未来

JavaScript历史

要了解JavaScript,首先要回顾一下JavaScript的诞生。

在上个世纪的1995年,当时的网景公司正凭借其Navigator浏览器成为Web时代开启时最著名的第一代互联网公司。

由于网景公司希望能在静态HTML页面上添加一些动态效果,于是一位叫布兰登·艾克(Brendan Eich)的哥们在两周之内设计出了JavaScript语言。你没看错,这哥们只用了10天时间。

这里放上一张Brendan Eich的照片,向大师致敬!(他后来曾担任过Mozilla公司的首席技术官和短暂的首席执行官)

为什么起名叫JavaScript?原因是当时Java语言非常红火,所以网景公司希望借Java的名气来推广,但事实上JavaScript除了语法上有点像Java,其他部分基本上没啥关系。

ECMAScript

因为网景开发了JavaScript,一年后微软又模仿JavaScript开发了JScript,为了让JavaScript成为全球标准,几个公司联合ECMA(European Computer Manufacturers Association)组织定制了JavaScript语言的标准,被称为ECMAScript标准。

所以简单说来就是,ECMAScript是一种语言标准,而JavaScript是网景公司对ECMAScript标准的一种实现。

那为什么不直接把JavaScript定为标准呢?因为JavaScript是网景的注册商标。

不过大多数时候,我们还是用JavaScript这个词。如果你遇到ECMAScript这个词,简单把它替换为JavaScript就行了。

JavaScript版本

JavaScript语言是在10天时间内设计出来的,虽然语言的设计者水平非常NB,但谁也架不住“时间紧,任务重”,所以,JavaScript有很多设计缺陷,后面会慢慢讲到。

此外,由于JavaScript的标准——ECMAScript在不断发展,最新版ECMAScript 6标准(简称ES6)已经在2015年6月正式发布了,所以,讲到JavaScript的版本,实际上就是说它实现了ECMAScript标准的哪个版本。

ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

二 一些重要的js基础概念

(1)函数声明与函数表达式

函数声明

如果function foo(){}被包含在一个函数体内,或者位于程序的最顶部的话,那它就是一个函数声明。函数的声明方式会得到提升,且如果有相同的函数,会覆盖。

函数表达式

如果function foo(){}是作为赋值表达式的一部分的话,那它就是一个函数表达式。

还有一种函数表达式就是被括号括住的(function foo(){})或者函数前加!等符号。

var bar = function foo(){}; // 表达式,因为它是赋值表达式的一部分
new function bar(){}; // 表达式,因为它是new表达式
(function foo(){}); // 函数表达式:包含在分组操作符内

区别

函数声明会在任何表达式被解析和求值之前先被解析和求值,即使你的声明在代码的最后一行,它也会在同作用域内第一个表达式之前被解析/求值

函数声明在条件语句内虽然可以用,但是没有被标准化,也就是说不同的环境可能有不同的执行结果,所以这样情况下,最好使用函数表达式

(2)作用域

JavaScript语言的作用域仅存在于函数范围中。这是必须要牢记的一点,还有一点重要的就是作用域的提升规则。

传统的类C,Java,它们的作用域是块级作用域(block-level scope), 花括号就是一个作用域。但是对于JavaScript作用域是函数级作用域(function-level scope),比如if条件语句,就不算一个独立的作用域。

在JavaScript中,每个函数被调用时都会创建一个全新的上下文环境。因此,在函数内部定义的变量和函数就只能在函数内部访问,在外部无法访问,那么在该上下文环境中,调用的函数就提供了一个非常方便的方式来创建私有成员。也就是解释了JavaScript的作用域是function-level。

变量提升

对JavaScript解释器而言,所有的函数和变量声明都会被提升到最前面, 并且变量声明永远在前面,赋值在声明过程之后

函数的声明方式主要由两种:声明式和变量式。

function foo1(){} //声明式

var a = function foo2() {} //变量式

声明式会自动将声明放在前面并且执行赋值过程。而变量式则是先将声明提升,然后到赋值处再执行赋值

带有命名的函数变量式声明,是不会提升到作用域范围内的,比如:

var baz = function spam() {};
baz(); // vaild
spam(); // ReferenceError "spam is not defined"

tips:

任何时候,请使用var声明变量, 并放置在作用域的顶端.

推荐使用JSLint工具帮助验证js语法的规范。

(3)立即执行函数(自执行函数)

如果想让函数定义完就立即执行:

// 这么写会报错,因为这是一个函数定义:
function() {}()

// 常见的:立即执行匿名函数:
(function () { /* code */ } ()); // 推荐使用这个
(function () { /* code */ })(); // 但是这个也是可以用的

// 但在前面加上一个布尔运算符就是表达式了,将执行后面的代码,也就合法实现调用,有好几种符号都可以保证匿名函数声明完就立即执行
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();

// 还有一个情况,使用new关键字,也可以用,但我不确定它的效率
// http://twitter.com/kuvos/status/18209252090847232

new function () { /* code */ }
new function () { /* code */ } () // 如果需要传递参数,只需要加上括弧()

在JavaScript中,如果我们需要实现block-level scope,有一种变通的方式,那就是通过自执行函数创建临时作用域:

function foo() {

var x = 1;

if (x) {

(function () {

var x = 2;

// some other code

}());

}

// x is still 1.

}

(4)闭包

闭包(closure):能够在外部访问函数内部的函数。它是Javascript语言的一个重点难点,也是js的特色,很多高级应用都要依靠闭包实现。

JavaScript变量的作用域是两种:全局变量和局部变量。Javascript语言的特殊之处,在于函数内部可以直接读取全局变量,函数外部无法读取函数内的局部变量。但通过闭包,可以在函数外面访问到内部的变量

闭包的用途

核心思想:闭包允许将函数与其所操作的某些数据(环境)关连起来。这类似于面向对象编程。在面对象编程中,对象允许我们将某些数据(对象的属性)与一个或者多个方法相关联。同时,闭包也是将函数内部和函数外部连接起来的一座桥梁

a.读取函数内部的变量

function f1(){
    var n=6;
    function f2(){
      alert(n); // 6
    }
  }

函数f2就包括在函数f1内部,f1内部的所有局部变量,对f2都可见。反之不行。这就是js特有的”链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。父对象的所有变量,对子对象都可见,反之则不成立。

b.使变量的值始终保持在内存中

function f1(){
    var n=6;

    function f2(){
      alert(n++);
    }
    return f2;  //注意这行代码
  }
  var result=f1();
  result(); // 6
  nAdd();
  result(); // 7

这里我们在外部调用result函数,可以不断跟踪内部的n值,实际上函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除

因为f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收

c.用闭包模拟私有方法

对于私有方法,js并不提供原生的支持,但是可以使用闭包模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。

下是一个经典的例子,展现了如何使用闭包来定义公共函数,且其可以访问私有函数和变量。这个方式也称为 模块模式(module pattern):

        /*使用闭包来定义公共函数,它可以访问私有函数和变量。这个方式也称为"模块模式",即定义模块的方法*/
        /**
         * 通过
         * var modulename = (function(){
         *             ...
         * })()
         * 定义一个模块或对象
         * */
        var countermodule = (function () {
            var praviteCounter = 0; /*模拟公有变量*/
            function changeBy(val) {  /*模拟公有函数,私有函数中可以去访问*/
                praviteCounter += val;
            }

            /*返回一个(模拟的私有函数)组成的对象,其中可以访问公有函数与变量*/
            return {
                increment: function () {
                    changeBy(1);
                },
                decrement: function () {
                    changeBy(-1);
                },
                value: function () {
                    return praviteCounter;
                }
            };
        })();

        alert(countermodule.value()); /*定义完模块对象后就可以调用它及其方法*/
        countermodule.decrement();
        alert(countermodule.value());
        countermodule.increment();
        countermodule.increment();
        alert(countermodule.value());

注意点:

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法:退出函数之前,将不使用的局部变量全部删除。

闭包会在父函数外部,改变父函数内部变量的值。所以,如果把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时一定要小心,不要随便改变父函数内部变量的值

闭包详细参考:

深入理解JavaScript系列(16):闭包(Closures)http://www.cnblogs.com/TomXu/archive/2012/01/31/2330252.html

(5)this对象

this是Javascript语言的一个关键字,代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。

比如:

function test(){
    this.x = 1;
  }

随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指调用函数的那个对象

主要分四种情况:

(1)全局作用域下的this指向全局对象(浏览器下就是window,node.js下就是global)

(2)一般函数的this

声明一个函数,直接调用,this仍然指向全局对象

function test(){
return this
}

test() //会得到全局对象

严格模式下指向undefined。

内部函数的 this 也绑定全局对象,应该绑定到其外层函数对应的对象上,这是 JavaScript的缺陷,用that替换

(3)作为对象方法的函数的this指向调用该方法的对象(最常用)

var o = {
props:6,
f:function(){
return this.props
}
}

o.f()  //得到6

(4)对象原型链上的this

var o = {
f:function(){
return this.a
}

var p = Object.Create(o)   //以o为原型创建p
p.a=6
p.f() //p调用了原型的方法,原型的方法中的this先指向直接调用它的对象,即p

(5)使用 new 实例化对象时(即作为构造函数):

this指向新创建的对象,如果对象没有被声明值,则this值为undefined。

function test(){
this.a = 1
}

var b = new test() //this指向新生成的b对象

(6)一些特别的函数call(), apply()以及bind(),它们的作用都是改变函数的调用对象,this会指向这些函数的第一个参数那个对象

Function.prototype 上的 call 或者 apply 方法 以及 with等

它指向 函数参数里传入的对象。此时this 将会被显式设置为函数调用的第一个参数

function add(c,d){
return this.a + this.b + c + d
}

var o = {a:1,b:2}

add.call(o,3,4) //add()里的this指向传入的o对象
add.apply(o,[3,4]) //add()里的this指向传入的o对象 

PS:js call()和apply()的区别:

区分apply,call就一句话,

foo.call(object1, arg1,arg2,arg3) == foo.apply(object1, arguments)==this.foo(arg1, arg2, arg3)

call, apply都属于Function.prototype的一个方法,所以每个Function对象实例,也就是每个方法都有call, apply属性。作用都是给一个函数传入对象和一些参数,给该对象执行该函数。

相同点:两个方法产生的作用是完全一样的:传递一些参数,调用该方法

不同点:方法传递的参数不同(call是分别传入各个参数,apply是将各个参数作为一个数据一次性传入)

bind()的this指向(有bind的会执行bind的值,没有bind的指向调用对象的值)

function f(){
return this.a
}

var g = f.bind({a:‘test‘})
console.log(g()) //会打印出test

var o = {a:6,f:f,g:g}

console.log(o.f(),o.g()) //会打印出6 test

(5)事件机制

几种常用的事件机制:

1.事件监听(addEventListener)

a.尽量不再使用html里onclick=”fuctionname()”或者在js里写element.onclick=function(){}

JS 代码与 HTML 代码耦合在一起,不便于维护和开发,且不好添加多个事件

b.element.addEventListener(事件名, 回调函数, use-capture) (removeEventListener()格式同样这三个参数)

use-capture参数: “捕获”阶段还是”冒泡”阶段中监听与触发(true捕获 false冒泡,默认false冒泡)

例子:

<button id="btn1">事件监听触发事件</button>

<script>
    btn1 = document.getElementById(‘btn1‘);
    //1.先获取元素,再写三个参数
    btn1.addEventListener(‘click‘, btn1Event, false); //2.第二个参数写已定义的函数名不要带括号,定义的函数再带括号会立即执行
    function btn1Event() {
        alert(‘btn1通过事件监听(在默认的冒泡阶段)触发了事件‘);
    }
</script>

事件触发过程:捕获(最不详细开始)->目标阶段->冒泡

2.事件代理

a.概念:
因为事件有冒泡机制,子节点的事件会顺着父级节点跑回去,
可以通过监听父级节点来实现监听子节点的功能,这就是事件代理

b.优势

(1)减少事件绑定,提升性能。之前如果需要绑定很多子节点的事件,现在只需要绑定一个父节点即可

(2)动态变化的 DOM 结构,仍可监听。当一个 DOM 动态创建之后,不会带有任何事件监听,除非重新执行事件监听函数,用事件监听无须担忧这个问题

例子1(jQuery实现事件代理,on()相当于addEventListener(),但on可以直接给子元素添加时间,更方便做事件代理,off相当于…):

<ul class="ul1">
    <li><a href="#">监听我</a></li>
    <li><a href="#">监听我</a></li>
    <li><a href="#">监听我</a></li>
    <li><a href="#">监听我</a></li>
    <li><a href="#">监听我</a></li>
    <li><a href="#">监听我</a></li>
    <li><a href="#">监听我</a></li>
    <li><a href="#">监听我</a></li>
</ul>

<a href="#" class="add-more-items">添加更多节点</a>

<script>
    //3.
    $(‘.ul1‘).on(‘click‘, ‘a‘, liEvent);
    function liEvent() {
        alert(‘事件代理:绑定事件给父节点‘);
    }

    //动态生成的元素也能立即拥有事件代理的事件
    $(‘.add-more-items‘).on(‘click‘, addMoreItems);
    function addMoreItems() {
        var item = $(‘<li><a href="#">我是动态生成的新节点也能调用通过父节点代理的事件</a></li>‘); //5.要动态生成的元素外边加上$(‘‘)才能调用jQuery的方法与代理的事件
        //一个元素里增加一个子节点用append()
        $(‘.ul1‘).append(item);
    }

    /**
     * jQuery on():
     * $(selector).on(event,childSelector,data,function,map)
     * event    必需。规定要从被选元素的一个或多个事件或命名空间,由空格分隔多个事件值
     * childSelector  可选。只能添加到指定的子元素上的事件处理程序
     * data      可选。规定传递到函数的额外数据。
     * function      可选。规定当事件发生时运行的函数。
     * map      规定事件映射 ({event:function, event:function, ...}),包含要添加到元素的一个或多个事件,以及当事件发生时运行的函数。
     * */

</script>

例子2(原生js实现事件代理,事件处理函数传入参数event,用event.target代表父元素里的子元素):

<div id="gaga">
    <a href="#" gaga="xixi">1</a>
    <a href="#">10</a>
    <a href="#">100</a>
    <a href="#">1000</a>
    <a href="#">10000</a>
    <a href="#">100000</a>
    <a href="#">1000000</a>
    <a href="#">10000000</a>
    <a href="#">100000000</a>
    <a href="#">1000000000</a>
    <a href="#">10000000000</a>
    <a href="#">100000000000</a>
</div>
<script>
    var gaga = document.getElementById("gaga");
    gaga.addEventListener(‘click‘, eventOfChild, false);

    function eventOfChild(event) {
        var event = event || window.event;
        var Target = event.srcElement || event.target;      // srcElement这个只是支持firefox

        alert(Target.tagName);
        if (Target.tagName.toUpperCase() == "A") {
            alert(Target.innerHTML);
        }
        return false;   //  防止跳转
    }
</script>

(6)事件的Event对象

当一个事件被触发时,会创建一个事件对象(Event Object),这个对象里包含了一些有用的属性或者方法。事件对象会作为第一个参数,传递给我们的回调函数。可以使用类似于下面的代码,在浏览器中打印出事件对象:

    var btn = document.getElementsByTagName(‘button‘);
    btn[0].addEventListener(‘click‘, function(event) {  //注意这块回调函数里的参数event就用来代表事件对象
        console.log(event);
    }, false);

比较常用的几个属性和方法:

type(string): 事件的名称,比如 “click”。

target(node): 事件要触发的目标节点。

currentTarget(node): 它就指向正在处理事件的元素:这恰是我们需要的。很不幸的是微软模型中并没有相似的属性, 你也可以使用”this”关键字。事件属性也提供了一个值可供访问:event.currentTarget。

bubbles (boolean): 表明该事件是否是在冒泡阶段触发的。

preventDefault (function): 这个方法可以禁止一切默认的行为,例如点击 a 标签时,会打开一个新页面,如果为 a 标签监听事件 click 同时调用该方法,则不会打开新页面。

stopPropagation (function): 很多时候,我们触发某个元素,会顺带触发出它父级身上的事件,这有时候是我们不想要的,大多数我们想要的还是事件相互独立。所以我们可以选择阻止事件冒泡,使用event.stopPropagation().

stopImmediatePropagation (function): 与 stopPropagation 类似,就是阻止触发其他监听函数。但是与 stopPropagation 不同的是,它更加 “强力”,阻止除了目标之外的事件触发,甚至阻止针对同一个目标节点的相同事件

cancelable (boolean): 这个属性表明该事件是否可以通过调用 event.preventDefault 方法来禁用默认行为。

eventPhase (number): 这个属性的数字表示当前事件触发在什么阶段。

0: none

1: 捕获

2: 目标

3: 冒泡

pageX 和 pageY (number): 这两个属性表示触发事件时,鼠标相对于页面的坐标。

isTrusted (boolean): 表明该事件是浏览器触发(用户真实操作触发),还是 JavaScript 代码触发的。

(7)原型,原型链与原型继承

a.原型:

在js中,每个函数都有一个原型属性prototype指向函数自身的原型,而由这个函数创建的对象也有一个proto属性指向这个原型,而函数的原型是一个对象,所以这个对象也会有一个__proto__**指向自己的原型,这样逐层深入直到Object对象的原型(null),这样就形成了原型链**。

tips:

prototype: 在函数身上,指向原型对象

proto: 在对象身上(包括函数创建的对象, 函数本身和原型对象),指向自身的原型

constructor: 在原型对象上,指向构造函数, 在多级继承的时候,指明构造函数方便在对象上扩展原型属性

Object.protp为null: 原型的顶端

原型的作用:

最主要的一点是数据共享创建对象时,把公共的方法和属性挂载到原型上,避免资源浪费

b.原型链

原型对象也有自己的原型,直到对象的原型为 null 为止(也就是没有原型)。这种一级一级的链结构就称为原型链。

原型继承的模型就是JavaScript实现继承的原理。真正形成原型链的是每个对象的proto属性,而不是函数的prototype属性,这是很重要。

属性查找

当查找一个对象的属性时,JavaScript 会向上遍历原型链,直到找到给定名称的属性为止。

到查找到达原型链的顶部, 也就是 Object.prototype, (因为Object的原型的proto是null) 但是仍然没有找到指定的属性,就会返回 undefined。

c.原型继承

原型继承实现方法:

(i)原型模式:

var Parent = function(){
    this.name = ‘parent‘ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(){
    this.name = ‘child‘ ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent() ;
var child = new Child() ;

console.log(parent.getName()) ; //parent
console.log(child.getName()) ; //child

这是最简单实现原型继承的方法,直接把父类的对象赋值给子类构造函数的原型,这样子类的对象就可以访问到父类以及父类构造函数的prototype中的属性

优点很明显,实现十分简单,不需要任何特殊的操作;同时缺点也很明显,如果子类需要做跟父类构造函数中相同的初始化动作,那么就得在子类构造函数中再重复一遍父类中的操作。

如果初始化工作不断增加,这种方式是不方便的。因此就有了下面一种改进的方式。

(ii)构造模式

var Parent = function(name){
    this.name = name || ‘parent‘ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent(‘myParent‘) ;
var child = new Child(‘myChild‘) ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

这样只需要在子类构造函数中执行一次父类的构造函数,同时又可以继承父类原型中的属性,这也比较符合原型的初衷,就是把需要复用的内容放在原型中,我们也只是继承了原型中可复用的内容。

原型继承与类继承的对比:

类继承 原型继承
类不可变。在运行时,无法修改或者添加新的方法 原型是灵活的。它们可以是不可变的也可以是可变的
类可能会不支持多重继承 对象可以继承多个原型对象
基于类的继承比较复杂。你需要使用抽象类,接口和final类等等 原型继承比较简洁。你只有对象,你只需要对对象进行扩展就可以了

(8)js异步编程方法

js的执行环境是”单线程”(single thread),就是一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务。

这种模式的好处是实现起来简单,执行环境相对单纯;坏处是只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。常见的浏览器无响应(假死),往往就是因为某一段Javascript代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行。

为解决这个问题,js将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

“同步模式”就是上一段的模式,后一个任务等待前一个任务结束,然后再执行。

“异步模式”则是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行。

“异步模式”非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,”异步模式”是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。

js”异步模式”编程有4种方法,理解它们可以写出结构更合理、性能更出色、维护更方便的js程序。

1.回调函数

function f1(callback){  //或者直接在()里放一个匿名function
   setTimeout(function(){
     //f1的任务代码
     callback();
  },1000);
 }
 f1(f2);

2.事件监听(和事件委托与代理不是一回事)

f1.on(‘click‘,f2);
 function f1(){
   setTimeout(function(){
     //f1的任务代码
     f1.trigger(‘click‘);
   },1000);
 }

3发布/订阅

“事件”,可以理解成”信号”。假定存在一个”信号中心”,某个任务执行完就向信号中心”发布”(publish)一个信号,其他任务可以向信号中心”订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做”发布/订阅模式”(publish-subscribe pattern),又称”观察者模式”(observer pattern)。

这个模式有多种实现,下面采用的是Ben Alman的Tiny Pub/Sub,这是jQuery的一个插件。

首先,f2向”信号中心”jQuery订阅”done”信号。

  

jQuery.subscribe("done", f2);

然后,f1进行如下改写:

  function f1(){
    setTimeout(function () {
      // f1的任务代码
      jQuery.publish("done");
    }, 1000);
  }

jQuery.publish(“done”)的意思是,f1执行完成后,向”信号中心”jQuery发布”done”信号,从而引发f2的执行。

此外,f2完成执行后,也可以取消订阅(unsubscribe)。

  jQuery.unsubscribe("done", f2);

这种方法的性质与”事件监听”类似,但是明显优于后者。因为我们可以通过查看”消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

4.Promises对象

Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供统一接口,是ES6标准中非常重要的一个新特性。

简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数

比如,f1的回调函数f2,可以写成:

  

f1().then(f2);

f1要进行如下改写(这里使用jQuery实现):

  function f1(){
    var dfd = $.Deferred();
    setTimeout(function () {
      // f1的任务代码
      dfd.resolve();
    }, 500);
    return dfd.promise; //返回一个函数的promise对象
  }

这样写的优点在于,回调函数变成了链式写法,程序的流程可以看得很清楚,而且有一整套的配套方法,可以实现许多强大的功能

比如,指定多个回调函数:

  

f1().then(f2).then(f3);

再比如,指定发生错误时的回调函数:

  f1().then(f2).fail(f3);

而且,它还有一个前面三种方法都没有的好处:如果一个任务已经完成,再添加回调函数,该回调函数会立即执行。所以,你不用担心是否错过了某个事件或信号。这种方法的缺点是编写和理解相对比较难

ES6的新特性

(1)promise

更多ES6的知识建议研究一下阮一峰前辈的《ECMAScript 6 入门》这本书(全书开源):

http://es6.ruanyifeng.com/#docs/promise

javascript学习资源

基础级

(1)js基本例子代码(页面下边还有 JavaScript 对象实例,JavaScript 浏览器对象实例,JavaScript HTML DOM 实例的链接):

http://www.runoob.com/js/js-examples.html

(2)gitbook上的前端工程师手册:

https://leohxj.gitbooks.io/front-end-database/content/

深入级

(1)汤姆大叔的深入理解JavaScript系列博客:

http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html

时间: 2024-07-31 13:12:49

前端学习总结(三)javascript——前端世界的精髓所在的相关文章

献给那些前端学习迷茫的人 -----前端开发必备的11项技能!!!

你也许会觉得前端开发是一个很简单的工作,对呀,你就是刚刚从网页设计转型过来的.但当你深入其中时,一定会发现好像前端开发不是那么简单,光网站性能优化.响应式.框架就让你焦头烂额, 确实,做前端开发就是先易后难,想成为一个优秀的前端开发,没有那么简单. 不过,天下事难则不会,会则不难,你只需要掌握11项技能就可以成为前端"大拿",下面,就告诉你这11项技能是什么?以及每项技能的要点,最后是相关的专业图书. 入门必备的技能:   第1项技能:HTML超文本标记语言: 技能要点: HTML文件

2018年web前端学习路线图,WEB前端开发新手学习路线

前端的的技术一直在变化,更新和变革,现在基本是三驾马车(vue,angualr,react)主导整个前端框架,但是无论对于新人或者有经验的程序员,这些知识在必须掌握,下面给大家一份详细的表单: html的语法.格局,常用的标签极端作用,了解标签的嵌套.学习运用firefox+firebug或许chrom的调试工具,可以运用这些工具调试html.css.js.断点调试.抓包 怎么引入css.js.了解id.class属性的区别,学会css的常用选择器,了解盒子模型(padding,margin,b

Web前端学习-第三课JavaScript篇

Q5:解释什么是引用类型?在使用引用类型的时候需要注意什么? 引用类型通常叫做类,也就是说,遇到引用值时,所处理的就是对象,就是公用. 类型:  object类型,应用于应用程序中存储和数据传输(创建object实例:new操作和队象字面量表示法): Array类型,JavaScript数组的每一项都可以保持任何类型的数据,数组大小可以动态调整: 转换方法,所有对象都具有tolocalstring()和tostring(),valueOf()方法,调用tostring()和value()方法会返

WEB前端学习笔记 三

接上一篇,web学习笔记 二,web学习笔记刚刚发出,就发现被其他网站采集了,在此感谢您的采集和转发,但请注明文章出自网知博学. 1.7  开发工具的选择 增强文本编辑器:EditPlus.Notepad++ 特点:比较小.占用系统资源比较少.代码颜色高亮显示.但没有代码自动补功能 IDE:(Integrated Development Environment,集成开发环境).IDE集成开发环境(简称IDE)开发环境就是指在开发软件的时候需要用到的软件.这些软件包括代码编辑器.编译器.调试工具和

前端学习(三)

1.绝对定位是不占用页面的结构的. 固定定位:(不管页面怎么拖动,图片永远都停留在屏幕的同一位置) Position:fixed; 相对定位: Position:relative; 相对定位会在页面占据位置,是相对于以前的位置进行了改变. 它是"想当年"定位. 总结:绝对定位相当于是一种特殊的浮动. 一般情况下定位的使用: 子绝父相: 将来在使用定位的时候最好在父元素中使用子元素定位,并且遵守一个约定,父元素使用相对定位,子元素使用绝对定位. CSS标签的嵌套关系 行内元素:span,

前端学习代码实例-JavaScript点击按钮弹出层效果

强大的效果其实都是由一个个简单的功能组合而成的,下面介绍一下如何实现点击一个按钮弹出一个层的效果. 代码实例如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="author" content="http://www.softwhy.com/" /> <title>web前端开发学习q群:73

前端学习代码实例-JavaScript 页面跳转效果

运用JavaScript,实现了在10秒之后跳转到指定页面. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="author" content="http://www.softwhy.com/" /> <title>web前端开发学习q群:731771211 详细教程讲解</title>

前端学习代码实例-JavaScript文本循环变色效果

介绍一下如何实现一行文本实现一段文本内容能够滚动循环变色效果. 此效果可以有效的提高美观度,使用户的印象更加深刻,当然也就能够使网站更加能够留住用户. 代码实例如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="author" content="http://www.softwhy.com/" /> &

前端学习代码实例-JavaScript动态创建div并写入文本

介绍一下如何利用JavaScript动态创建div元素,然后在其中写入文本. 代码实例如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" content="http://www.softwhy.com/" /> <title>web前端开发学习扣qun:731771211