ECMAScript 总结

<script>元素

  •    若果有src属性,元素内不可再包含JavaScript代码。如果包含了嵌入代码,则只会下载并执行外部脚本文件,嵌入的代码会被忽略。
  •    src属性可以指向其所在页面之外的其他域的完整URL。不过在访问自己不能控制的服务器上的JavaScript代码要多加小心。
  •    一般将JavaScript引用放在<body>元素的结束标签之前。因为如果将其放在<head>元素中,这就意味着必须等到所有的文件下载、解析和执行以后才能呈现页面内容,如果,外部文件有很多代码, 这无疑会导致页面显示延迟,  而延迟期间页面显示将是一片空白(浏览器在遇到<body>标签时才开始呈现内容)。
  • 尽可能的使用外部文件包含JavaScript代码,有点如下:

                    (1)可维护性,开发人员在不需要触及HTML标记的情况下,集中精力编辑JavaScript;

                    (2)可缓存,如果有两个页面使用同一个JavaScript文件,那么这个文件只需要下载一次;

                    (3)适应性,HTML和XHTML包含的外部文件的语法是相同的,无需使用hack等技术。

DOCTYPE声明及文档模式:

  • DOCTYPE声明处于文档最前面(<html>标签的前面),用于告诉浏览器用什么样的文档规范类型解析文档,该标签可以有三种DTD类型:

                      严格型:一种严格的DTD,不允许使用任何表现层的标识属性;

                      过渡性:一种不很严格的DTD;

                      框架集型 :如果使用框架式网页应当使用此方式。      

  • 文档模式:有标准模式与混杂模式,主要影响CSS内容的呈现,但在有些情况也会影响到JavaScript的解释器执行。

                     标准模式:以浏览器支持的最高标准运行。

                     混杂模式:页面以宽松的、向后兼容的模式显示,主要是考虑到一些老的浏览器的显示方式及怪异行为,防止老的站点无法工作。

        触发模式:浏览器根据Doctype是否存在以及使用哪种类型的DTD来决定以哪种方式解析文档。

                     触发标准模式:<!DOCTYPE html>;XHTML文档包含形式完整的Doctype;对于HTML 4.01的文档,包含严格型的DTD或包含过渡型的

                                         DTD和URI的DOCTYPE;

                     触发混杂模式:文档开始处无文档类型声明或文档声明形式不正确;过渡DTD而没有URI的DOCTYPE声明;

基本概念:

  • typeof操作符:此操作符可能返回下列某个字符串,

                "undefined"—如果这个值未定义;

                "boolean"—如果这个值是布尔值;

                "string"—如果这个值是字符串;

                "number"—如果这个值是数值;

                "object"—如果这个值对象或null;

                "function"—如果这个值是布尔值;

        instanceof操作符只能用于判断引用类型的值。注意:alert(null instanceof Object);//false  因为instanceof只能用于引用类型的值,在操作基本类型值时则返回false。

    var s1=new Object(5);
    alert(s1 instanceof Number);  //true
alert(typeof s1); //false var s2=new String("hello"); alert(s2 instanceof String); //true
alert(typeof s2); //false var s3="hello"; alert(s3 instanceof String); //false
alert(typeof s3); //string
好好理解下typeof 与instanceof用法区别(语法(variable instanceof constructor))

       在操作所有引 用类型值和Object构造函数时,instanceof始终返回true,因为基本的的引用类型都继承自Object。

  • null与undefined:当需要保存对象的变量没有真正保存对象,需要显式的初始化为null,没有初始化的变量则为undefined类型,没必要显式的初始化。

                     alert(null==undefined);    //true

                     alert(null===undefined)   //false

  • 浮点数的计算会产生舍入误差,因此永远不要测试某个特定的浮点数值。
      var a=0.1,
          b=0.2;
      if(a+b==0.3){   //不要做这种测试
        alert("You got 0.3");
      }
  • Infinity的值无法参与下次计算;NaN是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况:(1)任何涉及NaN的操作都会返回NaN;(2)NaN与任何值都不相等,包括NaN本身。
    alert(NaN==NaN); //false
    alert(10/0); //Infinity
    alert(0/0); //NaN
    
    alert(isNaN(NaN)); //true
    alert(isNaN("10")); //false 转化为10
    alert(isNaN("red")); //true 不能转化成数值
    alert(isNaN(true)); //true 被转换成1
  • 字符串是不可变得,也就是说,字符串一旦创建,它们的值就是不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量。
    var lang="Hello";
    lang=lang+" world";

    首先创建一个能容纳11个字符的新字符串,然后再在这个字符串中填充"Hello"和" world",最后是销毁原来的字符串"Hello"和" world"。这个过程是在后台发生的,因此这种方式效率低。

变量、引用类型

  • 变量:包含两种数据类型,基本类型值和引用类型值。

                 基本类型值:因为可以操作保存在变量中的实际值,因此是按值访问。复制基本数据类型的值的变量时时,会为新变量分配新的位置。参数的传递,如同基本类型值的复制。

                 引用类型值:是保存在内存中的对象。复制时,操作的是对象的引用,即复制的是指向对象的指针,两个变量实际上引用的是同一个对象。为对象添加属性时,操作的是实际 对象。参数传递,是按值传递的。

                 变量声明提升: 

                       var a=2;
                       function foo1(){
                           alert(a);
                       }

                       function foo2(){  
                           alert(a);
                           var a=3;
                       }
                       foo1(); //2
                       foo2(); //undefiend
由于变量在它所在的作用域中变量提升,所以foo2()函数相当于
function foo2(){
var a;
alert(a);
a=3;
} 所以foo2()的结果为undefined
                                
  • 引用类型:引用类型是一种数据结构,将数据和功能组织在一起。对象(引用类型的值)是某个引用类型的实例。

                 Object类型:创建Object实例的方式用两种。     

//new操作符后跟Object构造函数
  var person=new Object();
  person.name="liu";
  person.age=24;
//利用对象字面量创建对象时,实际不会调用Object构造函数
  var person={
      name:"liu",
      age:24
  };
//属性名也可以使用字符串
  var person={
      "name":"liu",
      "age":24
  };

访问对象属性的两种方法:alert(person.name); 和 alert(person["name"]);或var propertyName="name"; alert(person[propertyName]);(通过变量访问)
除非必须使用变量来访问属性,否则一般使用点表示法。

                Array类型:数组的每一项可以保存任何的数据类型,且数组的大小可以任意动态调整。数组的length属性不是只读的,可以通过length属性改变数组的大小, 也可方便的在数组末尾添加新项。数组的方法有:

                          转换方法:toString()返回字符串,toLocalString()返回字符串,valueOf()返回数组,join()返回以特定字符分割的字符串。它们将数组中为null与undefined的项 转化为空字符串。

                          栈与队列方法:push()从数组末尾添加项,返回修改后数组的长度,pop()移除数组的的末尾项,返回移除的项,shift()移除数组的 第一项并返回移除项,unshift()从数组前端添加项,返回修改后数组的长度。

                          重排序方法:reverse()反转数组项的顺序,sort()按升序排列数组项,sort()会调用每一项的toString()方法,然后比较得到的字符串,因此比较的是字符串。

                         操作方法:concat()创建新数组,不影响原数组,slice()获取数组项,不影响原数组。splice(),返回从原始数组中删除的项。

                         位置方法:indexOf() ,lastIndexOf(),返回查找项在数组中的位置。

                         迭代方法:every(),filter(),forEach(),map(),some()

                         归并方法:reduce(),reduceRight()

               Date类型:var now=new Date();在调用Date构造函数而不传递参数的情况下,新创建的对象自动获得当前日期和时间。

               RegExp类型:http://www.cnblogs.com/webliu/p/4665804.html

                Function类型:函数是对象,每个函数是Function类型的实例,函数名是指向函数的指针,不会与某个函数绑定。

                       (1)函数不介意传递进来的参数个数、类型,原因是参数在内部使用一个数组表示的,函数接收到的始终都是这个数组。 在函数体内可以通过arguments对象来访问这个数组。命名参数只是提供便利,并非必须。arguments的值永远与命名参数保持同步,但读取这两个值访问的是不同的内存空间,即它们的内存空间独立,但它们的值同步。如果传递进来一个参数,那么arguments[1]设置的值永远不会反应的命名参数中,因为arguments对象的长度是由传递进来的参数决定的,不是由定义时的命名参数决定的。没有传递值的命名参数将自动被赋予undefined。

                       (2)函数没有重载:一方面,函数无函数签名(参数类型,参数个数,参数顺序);另一方面,函数是对象,函数名是指针, 指针一次只能指向一个对象,因此后面的函数会覆盖前面的函数。

                       (3)函数声明与函数表达式:当前作用域中的函数声明会提升到作用域中最前面。函数表达式不能提升。

                       (4)函数内部属性:在函数内部,有两个特殊的对象,arguments和this。

                                    arguments是一个类数组对象,保存着穿入函数的所有参数。arguments对象有一个 callee的属性,它是个指针, 指向拥有这个arguments对象的函数,用它可以消除函数与函数名的紧密耦合。

                                     this:引用的是函数据以执行的环境对象。

                                     caller:保存着调用当前函数的函数的引用。如果在全局作用域中调用当前函数,它的值为null。

                       (5)函数属性和方法:length属性表示函数希望接收的命名参数的个数;prototype保存引用类型实例方法;

                                        apply()与call()用于在特定的作用域中调用函数,它们真正强大的地方是能够扩充函数赖以运行的作用域,它们的区别在于接收参数的方式。 它们的第一个参数都是其中运行函数的作用域,apply()的第二个参数为Array的实例或是arguments对象,call()把传递给函数的参数必须一一列举出来。使用这两个方法最大的好处是对象不需要与方法有任何的耦合。

                           var name="liu";
                           var o={name:"yuzi"};
                           function sayName(){
                               alert(this.name);
                           }
                           sayName();   //liu
                           sayName.apply(this);   //liu
                           sayName.call(o);    //yuzi

                        bind()这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。ECMAScript 5中定义的。

                    var name="liu";
                    var o={name:"yuzi"};
                    function sayName(){
                        alert(this.name);
                    }
                    var objectSayName=sayName.bind(o);
                    objectSayName();   //blue
objectSayName()函数的this值会指向o,因此即使在全局作用域中调用这个函数,也会显示"yuzi".
支持bind()的浏览器有IE9+、Firefox4+、Safari5.1+、Opera12+和Chrome

       基本包装类型:ECMAScript提供了3个特殊的引用类型:Boolean、Number和String。与其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为。每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象。从而让我们方便的调用一些方法来操作这些数据。基本包装类型与引用类型的主要区别是对象的生存期:使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中,而自动创建的基本包装类型的对象则只存在于一行代码的执行瞬间,然后立即被销毁。所以我们不能给基本类型的值添加属性

            var s1="Hello world";
            var s2=s1.substring(2);
第二个代码访问s1时,访问过程处于一种读取模式,也就是要冲内存中读取这个字符串的值,后台完成的操作:
(1)创建String类型的一个实例
(2)在实例上调用指定的方法
(3)销毁这个实例

                 Number类型:toFixed(小数位数) 按照指定的小数位返回数值的字符串,toExponential(小数位数)返回以指数表示法表示的数值的字符串形式, toPrecision(所有数字的位数)可能返回固定大小格式,也可能返回指数格式。

                 String类型:字符方法charAt(),charCodeAt();字符串操作方法slice(),substring(),substr();字符串位置方法indexOf(),lastIndexOf();trim()方法在ECMAScript 5中定义的,创建一个字符串副本,删除前置及后缀的所有空格(IE9+,Firefox3.5+,Safari5+,Opera10.5+,Chrome);字符串大小转换toLowerCase(), toUpperCase(),toLocaleLowerCase(),toLocaleUpperCase();模式匹配match(),search(),replace(),split(),local eCompare()方 法;String.fromCharCode();         

               单体内置对象:由ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在ECMAScript执行之前就已经存在了。

开发人员不必显示的实例化内置对象,因为它们已经被实例化。

                            Global对象:不属于其他任何对象的属性和方法最终都属于它的属性和方法。如isNaN(),isFinite(),parseInt(),parseFloat(); URI编码方法,encodeURI()主要用于整个URI,不会对本身属于URI的特殊字符编码,decodeURI(),encodeURIComponent()主要用于URI的某一段,对它发现的任何非标准字符编码,decodeURIComponent()。 eval()方法,是一个完整的ECMAScript解析器,只接受一个参数,它将传入的参数当做实际的ECMAScript解析,然后把执行的结果插入原位置。在eval()中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中,它们只在eval()执行的时候创建。

                             var a=2;
                             function foo1(){
                                 alert(a);
                                 var a=3;   //变量声明提升
                             }
                             function foo2(){
                                 alert(a);
                                 eval("var a=3;");  //变量声明没有提升
                             }
                             foo1();//undefined
                             foo2();//2
尽量减少对eval()的使用,一方面比较危险,防止代码注入;另一方面代码处于字符串中,影响性能

                     Global对象的属性:Undefined,Boolean,String,Number,NaN,Infinity,Object,Array,Function,Date,RegExp,Error。。。。。

                     window对象:ECMAScript没有指出怎么直接访问Global对象,但web浏览器将全局对象作为window对象的一部分加以实现的。另一种取得Global对象的方法是使用下面代码:

                         var global=function(){
                             return this;
                         }();
                         var a=2;
                         alert(global.a);  //2
在没有给函数明确指明this值的情况下,this的值等于Global对象。

                Math对象:Math.min(),Math.max(),Math.ceil(),Math.floor(),Math.round(),Math.random(),Math.abs(),Math.exp();Math.log(),Math.sqrt()。。。。

                               利用Math.random()从某个整数范围内随机选择一个值:

    值=Math.floor(Math.random()*可能值的总数+第一个可能的值)

               function selectFrom(lowerValue,upperValue){
                   var choices=upperValue-lowerValue+1;
                   return Math.floor(Math.random()*choices+1);
               }
               selectFrom(1,10);
               alert(num);   //介于1和10之间的一个数值
  • 在检测数据类型时也可使用:Object.prototype.toString.call( );
  • 作用域、内存问题、闭包

         (1)作用域:每个函数都有自己的执行环境,执行环境定义了变量或函数有权访问其他数据,每个执行环境都有一个与之关联的变量对象,环境中定义的所用变量和函数都保存在变量对象中。当代码在一个执行环境中执行时,会创建变量对象的作用域链,作用域链的用途是保证有权访问执行环境的所有变量和函数能够有序的访问,作用域的下一级是包含函数,全局执行环境的变量对象始终都是作用域链中的最后一个。每个执行环境都可以向上搜索作用域链,以查询变量和函数名。作用域链是一个指向变量对象的指针列表,它只是引用,并非实际包含变量对象。

                延长作用域:因为有些语句可以在作用域的最前端增加一个变量对象,该变量对象会在代码执行后被移除。

                         1)try-catch语句中的catch块:会创建一个新的变量对象,其中包含的是被抛出错误对象的声明;

                         2)with语句:会将制定的语句添加到作用域链中。

          (2)内存问题:JavaScript是一门具有自动垃圾收集机制的语言,开发人员一般不必操心内存管理的问题。但是JavaScript在进行内存管理及 垃圾收集时面临的问题还是不同。其中最主要的一个问题就是分配给web浏览器的可用内存量通常要比分配给桌面应用程序的少。这样主要是出于安全考虑,防止运行的JavaScript网页耗尽全部系统内存而导致系统崩溃。内存的限制会影响调用栈以及在一个线程中能够同时执行的 语句数量。因此确保占用最少的内存可以让页面获得更好的性能。最好的习惯是在数据不再用时对其解除引用,即将其设置为null来释放其引用。这一做法适用于大多数的全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用。解除引用并不意味着自动回收该值占用的内存,解除引用的真正目的是让值脱离执行环境 ,以便垃圾收集器下次运行时将其回收。垃圾收集机制的原理就是找出那些不再用的变量,释放其占用的内存,通常有两个策略:标记清除和引用计数。

           (3)闭包:如果一个函数能够访问另一个函数中的变量,则这个函数就是闭包。简单一点理解就是在函数a里面定义一个函数b,且在函数a外面能够 引用函数b,则函数b就是闭包。闭包主要用在两个方面,一个是需要访问私有变量,另一个是一些变量需要长期保存在内存中以备后面使用。然而闭包会携带包含它的函数的作用域链,因此会比其他函数占用更多的内存,过度使用闭包会导致内存占用过多而导致内存泄露。下面用一个例

                 子解释闭包:

              function a(){
                var name="liu";
                function b(){
                   alert(name);
                }
                return b;    //闭包
              }
              var result=a();
              result();

               当调用函数a时,会创建函数a的执行环境及作用域链,函数a中定义的变量和方法都保存在该执行环境关联的变量对象中,当函数a执行完毕后

               本应该将其执行环境销毁,但是由于闭包b的作用域链仍然需要引用这个变量对象,所以它的变量对象仍然保存在内存中,只有它的作用域链被销毁,

               直到闭包b被销毁后它的执行环境才能被销毁。

创建对象:           

  • 构造函数模式:
       function Person(name,age){
            this.name=name;
            this.age=age;
            this.sayName=function(){
                alert(this.name);
            };
        }
    
       var person=new Person("liu",23);

    用new操作符调用Person函数时,会经历一下4个步骤:
    (1)创建一个person新实例;
    (2)将构造函数Person的作用域赋给新对象person(因此this指向了这个新对象);
    (3)执行构造函数中的代码(为这个新对象添加属性);
    (4)返回新对象。

    使用构造函数的主要问题就是每个方法都要在每个实例上重新创建一遍。函数是对象,而创建多个完成同样任务的Function实例确实没有必要,因此可以考虑原型模式。

  • 原型模式:我们创建的每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法 (默认只有一个constructor属性,指向这个函数本身)。但当原型中包含引用类型值的属性时,一个实例对其引用类型值的修正会影响到其他实例所对应的属性值。所以,一般情况下需要组合使用构造函数模式和原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。
  function Person(name,age){
       this.name = name;
       this.age = age;
       this.friends = ["yiyi","koukou"];
   }
   Person.prototype.sayName = function(){
       return this.name;
   };
   
   var person1 = new Person("liuliu",24);
   var person2 = new Person("yuyu",25);
   person1.friends.push("huahua");
   console.log(person1.friends);   //["yiyi", "koukou", "huahua"]
   console.log(person2.friends);  //["yiyi", "koukou"]  没有受到person1.friends的影响


可以通过Person.prototype.isPrototypeOf(person1)来判断Person.prototype与person1是否存在[Prototype](无法访问到)的关系,
Object.getPrototypeOf(person1)返回[Prototype]的值Person.prototype


继承:

  • 原型链:每个构造函数都包含一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。如果让原型对象等于另一个类型的实例,那么原型对象将包含一个指向另一个原型的指针,这就是原型链的概念。当访问一个对象的属性时,首先在实例中搜索,如果没有找到,则沿着对象的原型链继续向上搜索,直到搜索到为止,如果该属性不存在,则返回undefined。
  • 借用构造函数:在子类型构造函数的内部调用超类型构造函数。需要call()与apply()配合使用。
  • 组合继承:原型链+借用构造函数,使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

         这里仅仅说继承的原理,具体需系统学习,很重要!!!

模块:

  • 可以使用函数和闭包来构造模块。模块是一个提供接口却隐藏状态与实现的函数或对象。通过使用函数产生模块,我们几乎可以完全摒弃全局变量的使用,避免污染全局环境。
  • 如果必须创建一个对象,并以某些数据对其进行初始化,同时还要公开一些能够访问这些私有数据的方法,使用模块。                                       

回调:

  • 网络上的同步请求会导致客户端处于假死状态,如果网络传输或服务器很慢,响应会慢到让用户不可接受。更好的方式是发起异步请求,提供一个当服务器的响应到达时随即触发的回调函数。异步函数立即返回,这样客户端就不会被阻塞。
原文地址:https://www.cnblogs.com/webliu/p/4410150.html