About JavaScript (Little Secrets You Don’t Know)

The kernel of mainstream browsers

IE:trident
Chrome:webkit/blink
firefox:Gecko
Opera:presto
Safari:webkit

The way to introduce JS

页面内嵌标签(可在head内或body内)
外部引入(常用方法)

JS basic syntax

变量:
变量声明:声明,赋值分解。单一var。 (var a=100)
命名规则:变量名必须以英文字母,*,$开头*
变量名可以包括英文字母,,$,数字
不可以用系统的关键字,保留字作为变量名


值类型(数据类型):
不可改变的原始值(栈数据):Number,Boolean,String,undefined,null
栈内存和栈内存之间赋值是拷贝(互不影响)
引用值(堆数据):array,Object,function,..data,RegExp


typeof()可查看数据类型(返回number,string,boolean,object,undefined,function)
类型转换:显式类型转换:Number(mix) 转换不了的就显示为NaN
                   parseInt(string,radix) string以radix进制为基底转换为10进制
                   parseFloat(string)
                   num.toString(radix) num转为radix进制
                   String(mix)
                   Boolean()
     隐式类型转换:isNaN() 先转为number,再与NaN比较
                   ++/-- +/-(正负)转为数字类型
                   + 转为字符串
                   - * / %  转为数字类型 '1'*1=1
                   && || !
                   < > <= >= 字符串与数字比较,转为数字再比较。字符串与字符串比较,比较ascii码
                   == != 转为数字再比较
不发生类型转换:===,!==


语句的基本规则:
每一句后面要以分号结束(除函数,for语句,if语句...)
js语法错误(低级错误,逻辑错误)会引发后续代码终止,但不会影响其他js代码块
书写格式要规范,'=+/-'两边要有空格

运算操作符:
'+':数学运算,字符串连接。
    任何数据类型加字符串都等于字符串
'-','*','/','%','=','()':'()'优先级最高,'='优先级最低
 '++','--','+=','-=','/=','*=','%='
e.g. var a = 10 ; var b = ++a - 1 + a++ (此时b等于21,a等于12)
 var a = 1 ; var b = a-- + --a (此时b等于0(先算--a,然后b=0+0=0),a等于-1) 

比较运算符:'>','<','==','>=','<=','!='(比较结果为boolean值) NaN!=NaN

逻辑运算符:'&&'(碰到假就停),'||'(碰到真就停),'!'(运算结果为真实的值)
e.g. var a = 1 && 2+2 (a为4)
    var a = 0 && 2+2 (a为0)
    var a = 2+1 && 0 (a为0)
    var a = 1 || 3 (a为1)
    var a = 0 || 2 (a为2)
    (window.foo || (window.foo='bar')) (window.foo的值为bar(先看括号的))

被认定为false的值:undefined,null,NaN,'',0,false

条件语句:if,if else if,switch case,break,continue
循环语句:for,while,do while

function

定义:函数声明(function xxx(){})
函数表达式(命名函数表达式:var test=function xxx(){}, 匿名函数表达式(常用):var test=function (){})
组成形式:函数名称(小驼峰原则 如theFirstName)
         参数:形参(获取形参长度:函数名.length),实参(arguments表示的是实参列表,获取实参长度:arguments.length)
         返回值


e.g.1 求5的阶乘(递归)
    function mul(n){
        if(n==1 || n==0)
        {
            return 1
        }
        return n*mul(n-1);
    }
    mul(5);

Scope

作用域定义:变量和函数生效的区域(存储了运行期上下文的集合)
作用域的访问顺序:函数里面的可以访问外面的值(里面的可以访问外面的,外面的不能访问里面的)
函数被定义时,会在作用域链中生成一个GO对象
函数被执行时,会在作用域链中多生成一个AO对象,此时0号位置是AO,1号位置是GO


当多个函数为包含关系时,各函数作用域链的变化:
 e.g. function a(){
        function b(){
          function c(){
          }
          c();
        }
      b();
    }
    a();

   a defined  a.[[scope]] --> 0:GO
   a doing  a.[[scope]] --> 0:aAO 1:GO
   b defined  b.[[scope]] --> 0:aAO 1:GO
   b doing  b.[[scope]] --> 0:bAO 1:aAO 2:GO 
   c defined  c.[[scope]] --> 0:bAO 1:aAO 2:GO
   c doing  c.[[scope]] --> 0:cAO 1:bAO 2:aAO 3:GO
   
   with会改变作用域链:with(访问的对象){执行的代码} 
                      若访问的对象没有对应的变量,则在原本执行的这个函数体中找变量。
   e.g.
   var obj={name:'obj'};
   function test(){
        var age=123;
        var name='scope';
        with(obj){
               console.log(name);  //输出obj
               console.log(age);  //输出123
        }
   }       

JS trilogy (focus on pre-compilation)

语法分析
预编译(发生在函数执行的前一刻)
解释执行


预编译:函数声明整体提升
        变量声明提升
预编译前奏:imply global暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量为全局对象(window)所有(var a=b=123(b为经声明就赋值,归window所有。所以window.a为undefined,window.b为123))
       一切声明的全局变量,全是window的属性(var a=123 => window.a=123)
预编译过程四部曲:创建AO对象
             找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
             将实参值和形参统一
             在函数体里面找函数声明,值赋予函数体

e.g. function fn(a){                 1.创建AO对象(执行期上下文)
    console.log(a);               2.找形参和变量声明
    var a=123;                      AO{a:undefined,b:undefined}
    console.log(a);               3.将实参和形参统一
    function a(){};                 AO{a:1,b:undefined}
    console.log(a);               4. 找函数声明
    var b=function(){}              AO{a:function a(){},b:undefined,d:function d(){}}
    console.log(b);
    function d(){}
    }
    fn(1)

所以第一个console.log(a)打印的是function a(){}
 此时执行var a=123,所以此时AO{a:123,b:undefined,d:function d(){}}
第二个console.log(a)打印的是123
此时不用再看function a(){}(因为预编译已经看过了)
第三个console.log(a)打印的是123
此时执行var b=function (){},所以此时AO{a:123,b:function (){},d:function d(){}}
第四个console.log(b)打印的是function (){}

若一个变量未经声明就赋值,会把此变量放到GO对象(window)中

e.g.1 console.log(test);
   function test(test){
    console.log(test);
    var test=234;
    console.log(test);
    function test(){};
   }
   test(1);
   var test=123;

   先创建GO对象:GO{test:function (){...}}
   再创建AO对象:AO{test:function (){}}

   所以第一个console.log(test)为function(){}
   第二个为function(){}
   执行var test=234,所以AO{test:234}
   第三个为234

   e.g.2 global=100;
         function fn(){
         console.log(global);
         global=200;
         console.log(global);
         var global=300;
         }
         fn();
         var global;

         先创建GO:{global:100}
         再创建AO:{global:undefined}
         此时第一个输出为undefined(AO中有的就先从AO中找)
         再执行global=200;此时AO:{global:200}
         所以第二个输出为200

  e.g.3 function test(){
            console.log(b);
            if(a){
                var b=100;
                }
                console.log(b);
                c=234;
                console.log(c);
                }
                var a;
                test();
                a=10;
                console.log(c);

                先创建GO:{a:undefined}
                再创建AO:{b:undefined}
                第一个输出为undefined
                此时a为undefined,不能执行var b=100
                所以第二个输出为undefined
                执行c=234,AO里没有c,则把变量c添加到GO中,此时GO{a:undefined,c:234}
                第三个输出为234
                再执行a=10,此时GO{a:10,c:234}
                第四个输出为234

 e.g.4 var x=1;
       if(function f(){}){
          x+=typeof f;
       }
       console.log(x);

       因为(function f(){})为表达式,所以f是undefined
       所以x输出为1undefined
       (未经声明的变量放在typeof中不会报错,为undefined)


   关于GO和AO对象,若AO上没有相应变量,则再去GO上找
   预编译时会忽略掉判断的语句
   GO和AO对象实则是执行期上下文

Execute function immediately

立即执行函数主要针对初始化功能的函数(执行完就被销毁)
(function (形参(可有可无)){}(实参(可有可无)))
只有表达式才能被执行符号执行
如:var test=function (){}();
    +function test(){}();
    (function test(){})();

Closure (emphasis)

闭包的生成:当内部函数被保存到外部的时候
闭包的防范:闭包会导致多个执行函数共用一个公有变量,如果不是特殊需要,应尽量防止这种情况发生
闭包的作用:实现公有变量(函数累加器)
           可以做缓存(存储结构)
           可以实现封装,属性私有化
           模块化开发,防止污染全局变量
可用立即执行函数解决闭包

e.g. 给4个li绑定点击事件
    var li=document.getElementByTagName('li');
     for(var i=0;i<li.length;i++){
            (function (j){
                li[j].onclick=function(){
                console.log(j)
            }(i))
    }

Object

属性的增,删,改,查:
增:对象名.属性名=属性值
删:delete 对象名.要删除的属性名
改:对象名.要修改的属性名=新的属性值
查:对象名.要查看的属性名

对象的创建方法:
1. 对象字面量/对象直接量 var obj={}
2. 构造函数(大驼峰命名规则:如TheFirstName):系统自带的构造函数Object():var obj=new Object()
                                             自定义:function 函数名(){this.属性名=属性值...}
                                                    var 对象名= new 函数名()
3. var obj=Object.create(原型/null)(创建对象的同时可自定义原型)

改造函数的内部原理:1.在函数体最前面隐式的加上this={}(AO:{this:{xxx}})
                   2.执行this.xxx=xxx
                   3.隐式的返回this(构造函数中返回值不能返回原始值,会忽略掉,自动返回this)
 
 包装类:new String()
         new Boolean()
         new Number()
 e.g. var str='abc';
      str+=1;
      var test=typeof(str);
      if(test.length==6){
            test.sign='typeof的返回结果可能为String';
            (原始值赋属性值要调用包装类,赋和没赋一样)
            //new String(test).sign='xxx' 随后delete
       }
       //new String(test).sign(为undefined)
       cosnole.log(test.sign);
        输出的结果为undefined

prototype

原型是function对象的一个属性,它定义了构造函数制造出来的对象的公共祖先
通过该构造函数产生的对象,可以继承该原型的属性和方法
原型也是对象
利用原型可以提取公有的属性
对象查看原型:隐式属性__proto__  (函数名.prototype)
对象查看对象的构造函数:constructor
判断是否为自身的属性:对象名.hasOwnProperty(属性值)

e.g. Person.prototype.name='sunny'
     function Preson(){}
     var person=new Preson();
     Preson.prototype.name='cherry';
     此时 person.name='cherry'
     
     Person.prototype.name='sunny'
     function Preson(){}
     var person=new Preson();
     Preson.prototype.name={name:'cherry'}
     类似于
     var obj={name:'a'};
     var obj1=obj;
     obj={name:'b'};(新开的空间)
     所以此时 person.name还是等于'sunny'(此时的__proto__还是指向原来的Person.prototype)

原型链中绝大多数对象最终都会继承自Object.prototype

undefined和null没有原型

改变this指向:call,apply
call:原来的函数名.call(this要指向的函数名,参数)
apply:原来的函数名.apply(this要指向的函数名,[参数])

call和apply的区别:传参数的形式不同
                   call是要把实参按照形参的个数传进去
                   apply是传一个arguments

Inheritance mode

1. 传统形式(原型链):过多的继承了没用的属性
2. 借用构造函数:不能继承借用构造函数的原型
                每次构造函数都要多走一个函数
3. 共享原型:不能随便改动自己的原型
4. 圣杯模式
   第一种写法:
   function inherit(Target,Origin){
        function F(){};
        F.prototype=Origin.prototype;
        Target.prototype=F.prototype;
        Target.prototype.constructor=Target;
        Target.prototype.uber=Origin.prototype;
    }
   第二种写法:
   var inherit=(function (){
        var F=function (){};
        return function(Target,Origin){
             F.prototype=Origin.prototype;
             Target.prototype=F.prototype;
             Target.prototype.constructor=Target;
             Target.prototype.uber=Origin.prototype;
        }
   }())

Namespaces

管理变量,防止污染全局,适用于模块化开发

可使用闭包的方法实现变量私有化,从而防止变量污染
var name='bcd';
var init=(function (){
    var name='abc';
    function callName(){
        console.log(name);
    }
    return function(){
        callName();
    }
}())
init();
此时打印出来的name还是abc

还可使用Webpack等

JS implements chain call mode (imitating JQ)

如:obj.eat().smoke().drink()

在每个方法后面return this

Object enumeration

数组遍历:for循环数组长度

对象遍历:for in循环
for(var xxx(自定义) in 对象名){console.log(对象名[xxx])}

Three ways to distinguish between arrays and objects

方法一:instanceof
判断A对象是不是B构造函数构造出来的(判断A对象的原型链上有没有B的原型):A instanceof B
数组:[] instanceof Array --> true 
对象:{} instanceof Object --> true

方法二:constructor
对象:obj={},obj.constructor --> function Object()
数组:arr=[],arr.constructor --> function Array()

方法三:toString()
数组:Object.prototype.toString.call([]) --> [object Array]
对象:Object.prototype.toString.call([]) --> [object Object]

About this

函数预编译过程 this -> window
全局作用域里 this -> window
call/apply可改变函数运行时的指向
obj.func();   func()里面的this指向obj(谁调用this指向谁)

e.g.1 var name='222';
     var a={
        name:'111',
        say:function(){
            console.log(this.name);
         }
      }
      var fun=a.say;
      fun();  //全局调用
      a.say(); //a调用
      var b={
          name:'333',
          say:function(fun){
                fun();
          }
       }
       b.say(a.say);  //没有说明是this调用,只是把a.say这个方法传进来执行(相当于借用这个方法在b里面执行),此时还是全局调用
       b.say=a.say;
       b.say();  //此时为b调用
       
       依次的输出结果为:222,111,222,333
  
 e.g.2 var foo=123;
        function print(){
            this.foo=234;
            console.log(foo);
        }
        print();
        
        执行this.foo=234时,此时this指向window,改变GO中foo的值为234
        所以输出结果为234
        
  若把上题的print()改为new print():由于此时的this=Object.create(print.prototype),this不指向window了
  所以此时输出结果为123
  
  e.g.3 var a=5;
        function test(){
            a=0;
            alert(a);
            alert(this.a);
            var a;
            alert(a);
        }
        test();  依次输出0 5 0
        new test(); 依次输出0 undefined(this上面没有a) 0
        
  e.g.4 var bar={a:'002'};
        function print(){
            bar.a='a';
            Object.prototype.b='b';
            return function inner(){
            console.log(bar.a);
            console.log(bar.b);
            }
        }
        print()();(相当于先返回一个函数,再进行函数执行)  依次输出为a,b
        
       
arguments.callee:会指向自己的引用。引用该函数的函数体内的当前正在执行的函数。当函数名称未知时,例如在没有名称的函数表达式中(也称为“匿名函数”),此功能很有用。
e.g. var num=(function(n){
        if(n==1){
            return 1
         }
         return n * arguments.callee(n-1)
     }(100))
                    
 func.caller:返回调用指定函数的函数
 
 注意:arguments.callee和func.caller在严格模式下都不能使用

clone

 浅层克隆和深层克隆的区别:
 浅层克隆:当克隆引用值时,两个对象会共用一个引用地址,造成相互的干扰,即我改,它也改
 深层克隆:克隆出来的对象和原来的对象是相互独立的,互不影响。也就是对新对象的修改都不会反映到原对象中
    
 浅层克隆:
    function clone(origin,target){
        var target=target || {};
        for(var prop in origin){
            target[prop]=origin[prop];
          }
          return target;
     }

 深层克隆:
 	实现步骤:
    1. 判断是不是原始值 (typeof)
    2. 判断是数组还是对象(instanceof,toString(推荐使用),constructor)
    3. 建立相应的数组或对象
    4. 递归
    function deepClone(origin,target){
            var target=target || {};
            var toStr=Object.prototype.toString;
            var arrStr="[object Array]";
            for(var prop in origin){
                   if(origin.hasOwnProperty(prop)){  //要克隆自身的属性
                        if(origin[prop] !== "null" && typeof(origin[prop])=='object'){
                             if(toStr.call(origin[prop])==arrStr){ //克隆的属性是个数组 
                                 target[prop]=[];  //新建一个数组
                             }
                             else{  //克隆的属性是个对象
                                 target[prop]={};   //新建一个对象
                             }
                            deepClone(origin[prop],target[prop]) 
                        }
                        else{  //原始值直接拷贝
                            target[prop]=origin[prop];
                        } 
             }    
        }

Array and class array

创建数组的方法:
var arr=[];
var arr=new Array(1,2,3,4);
var arr=new Array(10);  
new Array():当为一个参数n时表示新开一个长度为n的数组空间
             当为多个参数时表示数组里的值
  
 数组的读和写:
 arr[n]:读,但不可以溢出读,要不然结果为undefined
 arr[n]=xxx:写,可以溢出写
 
 数组常用的方法:
 会改变原数组的:push(在数组末尾添加数据)
                pop(剪切数组末尾的数据)
                shift(剪切数组前面的数据)
                unshift(在数组前面添加数据)
                sort(数据排序,默认升序):arr.sort(function(a,b){return x(若x为负数时,那么前面的数放在前面。若x为正数,那么后面的数放在前面。若x为0,则不动。可直接利用return a-b进行升序,return b-a进行降序)})
                                          可利用sort生成一个乱序数组:xxx.sort(function(){ return Math.random()-0.5 })
                reverse(数据反转倒序)
                splice(切片):splice(从第几位开始,截取多少长度,在切口处添加新的数据)
 不会改变原数组的:concat(数组拼接)
                  join(连接):join('x')按照'x'来连接数组里的数据
                  split(拆分):solit('x')按照'x'来拆分数组里的数据,拆分为字符串
                  toString(变成字符串)
                  slice(截取):slice(从该位开始截取,截取到该位)
               
  
 类数组:
 可以利用属性名模拟数组的特性
 可以动态的增长length属性
 如果强行让类数组调用push方法,则会根据length属性值的位置进行属性的扩充
 属性要为索引(数字)属性,必须有length属性,最好加上push  
 e.g. var obj={
        "2" : "a",
        "3" : "b",
        "length" : 2,
        "push" : Array.prototype.push
        }
        obj.push('c'); 
        obj.push('d');
        输出结果obj
        
       push的原理是:
       Array.prototype.push=function(target){
            this[this.length]=target;
            this.length++;
       }
       所以obj.push('c'),在这个obj中相当于obj[obj.length(即为2)]='c',然后再length++
       随后obj.push('d'),相当于obj[3]='d'
       所以输出的obj为{
                        "2" : "c",
                        "3" : "d",
                        "length" : 2,
                        "push" : Array.prototype.push
                      }
        
        
 实现数组的去重(在原型链上实现):
 Array.prototype.unique=function(){
        var temp={};
        var arr=[];  //放置去重完后的数组
        var len=this.length;
        for(var i=0;i<len;i++){
               if(!temp[this[i]]){  //若没有对应的属性值
                      temp[this[i]]='abc';
                      arr.push(this[i]);
                }
       }
     return arr;  
 }

Try…catch

在try里面的发生错误,不会执行错误后的try里面的代码,但还会依然执行外面的代码
try{

}catch(e){

}

常见的错误信息(error.name的六种对应信息):
1. EvalError:eval()的使用与定义不一致
2. RangeError:数值越界
3. ReferenceError:非法或不能识别的引用数值
4. SyntaxError:发生语法解析错误
5. TypeError:操作数类型错误
6. URLError:URL处理函数使用不当  

es5 strict mode

"use strict"(要写在页面逻辑的最顶端)
不再兼容es3的一些不规则语法,使用全新的es5规范。
两种用法:全局严格模式
         局部函数内严格模式(推荐)
"use strict"就是一行字符串,不会对不兼容严格模式的浏览器产生影响。
不支持with,arguments,callee,func,caller,变量赋值前必须声明,局部this必须被赋值(Person.call(null/undefined)赋值什么就是什么),拒绝重复属性和参数

DOM manipulation

DOM定义了表示和修改文档所需的方法。DOM对象即为宿主对象,由浏览器厂商定义。用来操作html和xml功能的一类对象的集合。

对节点的增删改查
查: (除了getElementsById匹配出来是一个,其他匹配出来都是类数组(要在末尾加上[n(0,1,2,...)]))
     document.getElementById('id名') 根据元素id匹配相应的元素(在IE8以下的浏览器不区分id名大小写)
     document.getElementsByTagName('标签名') 根据元素标签名匹配相应的元素
     document.getElementsByName('name名') 根据元素的标签name匹配相应的元素
     document.getElementsByClassName('类名') 根据元素标签名匹配相应的元素(在IE8和IE8以下的浏览器没有,可以多个class一起)
     document.querySelector('CSS选择器字符串') 匹配指定 CSS 选择器的一个元素(在IE7和IE7以下的版本没有)不能实时更新
     document.querySelectorAll('CSS选择器字符串') 匹配指定 CSS 选择器的一组元素(在IE7和IE7以下的版本没有)不能实时更新
增:
    document.createElement():创建元素节点
    document.createTextNode():创建文本节点
    document.createComment():创建注释节点
    document.createDocumentFragment():创建虚拟的节点对象(可在其中添加DOM节点以构建屏幕外DOM树)
插:
    要添加到的区域.appendChild(要添加的节点):从后面添加一个子节点到页面(是一种剪切操作)
    要添加到的区域.insertBefore(要添加的节点,要在此节点前添加的节点):从前面添加一个子节点到页面
    封装函数insertAfter():
    Element.prototype.insertAfter(targetNode,afterNode){
        var beforeNode=afterNode.nextElementSibling;
        if(beforeNode==null){  //若是最后一个节点,则直接在其后面插入
            this.appendChild(targetNode);
        }
        else{   //在要插的下一个节点前插入(相当于插在要插节点的后面)
            this.insertBefort(targetNode,beforeNode);
       }
    }
删:
    父节点.removeChild():父节点移除子节点(剪切操作)
    节点.remove():节点自己移除自己(删除操作)
替换:
    父节点.replaceChild(new,origin):替换子节点
       
遍历节点树:
parentNode:父节点(最顶端的parentNode为#document)
childNodes:子节点们
firstChild:第一个子节点
lastChild:最后一个子节点
nextSibling:后一个兄弟节点
previousSibling:前一个兄弟节点

节点的类型:元素节点(1)
           属性节点(2)
           文本节点(3)
           注释节点(8)
           document(9)
           DocumentFragment(11)
           
基于元素节点树的遍历:
parentElement:返回当前元素的父元素节点(IE不兼容)
children:只返回当前元素的元素子节点
node.childElementCount(相当于node.children.length):返回当前元素节点的子元素个数
firstElementChild:返回第一个元素节点(IE不兼容)
lastElementChild:返回最后一个元素节点(IE不兼容) 
nextElementSibling:返回后一个兄弟元素节点
previousElementSibling:返回前一个兄弟元素节点
(IE不兼容都是指IE9和IE9以下浏览器不兼容)

节点的四个属性:
nodeName:元素的标签名,以大写形式表示,只读
nodeValue:Text节点或Comment节点的文本内容,可读写
nodeType:该节点的类型,只读
attribute:Element节点的属性集合

节点的一个方法:Node.hasChildNodes()(判断是否有子节点)

Element节点的一些属性:
innerHTML:添加HTML结构内容
innerText/textContent:添加文本结构内容(innerText 火狐不兼容,textContent IE老版本不好使)

Element节点的一些方法:
ele.setAttribute(属性名,属性值):设置节点属性
ele.getAttribute(属性名):获取节点属性

date object

系统提供的
var date=new Date();
详细功能方法可参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date

Timer

设置定时器:
定时器名=setInterval(function(){
      .....
},1000)

清除定时器:
clearInterval(定时器名)

设置延时器:
延时器名=setTimeout(function(){
    .....
},1000)  //时间间隔在window系统里面最小是15ms,在MAC系统里面最小是10ms(即使设置为0,也会按照最小的时间间隔执行)

清除延时器:
clearInterval(延时器名)

关于setTimeout和setInterval的最小值,详细可参考网址:https://www.cnblogs.com/daysme/p/6207495.html

Get window properties, dom size

查看滚动条的滚动距离:
window.pageXOffset/pageYOffset(横向/纵向) (IE8/IE8以下不兼容)
document.body/documentElement.scrollLeft/scrollTop (兼容性混乱)
(使用的时候可两者相融合(相加),适应兼容性)
如:document.body.scrollLeft+document.documentElement.scrollLeft

封装兼容性方法:getScrollOffset(){
                    if(0 && window.pageXOffset){
                        return{
                            x:window.pageXOffset,
                            y:window.pageYOffset
                        }
                     }  
                     else{
                         return{
                            x:document.body.scrollLeft+document.documentElement.scrollLeft,
                            y:document.body.scrollTop+document.documentElement.scrollTop
                         }
                     }
               }
       
       
查看视口的尺寸:
window.innerWidth/innerHeight(查看窗口的宽高)(IE8/IE8以下不兼容)
document.documentElement.clientWidth/clientHeight(标准模式下任何浏览器兼容)
document.body.clientWidth/clientHeight(适用于怪异模式下的浏览器)
判断是否为怪异模式:document.compatMode:CSS1Compat(正常模式),backCompat(怪异模式)

封装兼容性方法:getViewportOffset(){
                    if(0 && window.innerWidth){
                        return {
                            w:window.innerWidth,
                            h:window.innerHeight
                        }
                    }
                    else{
                        if(document.compatMode==='BackCompat'){  //怪异模式下
                            return {
                                w:document.body.clientWidth,
                                h:document.body.clientHeight
                            }
                        }
                        else{
                            return {
                                w:document.documentElement.clientWidth,
                                h:document.documentElement.clientHeight,
                            }
                        }
                    }
              }


查看元素的几何尺寸:domEle.getBoundingClientRect()
                   返回一个对象,包含left,top,right,bottom,width,height等属性(但width,height在IE老版本中没有显示)
                   返回结果不实时
                
查看元素的尺寸:dom.offsetWidth,dom.offsetHeight
查看元素的位置:dom.offsetLeft,dom.offsetTop
               dom.offsetParent(返回最近的有定位的父级,若无则返回body。body.offsetParent返回null)
               
封装函数:getElementPosition(求相对于文档的坐标)

使滚动条滚动的三个方法:scroll(x,y):滚动到...
                      scrollTo(x,y):滚动到...
                      scrollBy(x,y):滚动了...(会累加)

Scripted CSS

读写元素css属性:
dom.style.prop
可读写行间样式,没有兼容性问题,碰到float这样的保留字属性,前面应加css(即float --> cssFloat)
复合属性尽量拆解
组合单词变成小驼峰式写法

查询计算样式:
window.getComputedStyle(ele,null)[prop]
计算样式只读
返回的计算样式的值都是绝对值,没有相对单位
IE8及IE8以下不兼容 
null用在获取伪元素时,改变null(如:window.getComputedStyle(ele,'after').width)

查询样式(IE8及IE8以下可用,IE独有):ele.currentStyle.prop
计算样式只读
返回的计算样式的值不是经过转换的绝对值

封装兼容性方法:getStyle(ele,prop){
                    if(window.getComputedStyle){
                        return window.getComputedStyle(ele,null)[prop]; 
                    }
                    else{
                        return ele.currentStyle.prop;
                    }
                }

event

 绑定事处理函数:
 ele.onxxx=function(e){} 兼容性很好,但是一个元素的同一个时间上只能绑定一个处理程序  this指向元素本身
 obj.addEventListener(type,fn,false){} IE9以下不兼容,可为一个事件绑定多个处理程序  this指向元素本身
 obj.attachEvent('on'+type,fn){} IE独有,一个事件同样可以绑定多个处理程序  this指向window
 
 封装兼容性的函数方法:addEvent(ele,type,handle){
                        if(ele.addEventListener){
                            ele.addEventListener(type,handle,false);
                        }
                        else if(ele.attachEvent){
                            ele.attachEvent('on'+type,function(){
                                handle.call(ele);
                            })     
                        }
                        else{
                            ele['on'+type]=handle;
                        }
                     }
                     
解除事件处理函数:
ele.onclick=false/null/''
ele.removeEventListener(type,fn,false)
ele.detachEvent('on'+type,fn)
 
事件处理模型:
事件冒泡:结构上(非视觉上)嵌套关系的元素,会存在事件冒泡的功能,即同一事件,自子元素冒泡向父元素(自底向上)
(focus,blur,change,submit,reset,selectd等事件不冒泡)
取消冒泡事件发生:e.stopPropagation()(不支持IE9以下的版本)
                 e.cancelBubble=true(IE独有)

事件捕获:结构上(非视觉上)嵌套的关系的元素,会存在事件捕获的功能,即同一事件,自父元素捕获至子元素(事件源元素) (自顶向下) IE没有捕获事件
把addEventlistener中第三个参数改为true可触发事件捕获

触发顺序:先捕获后冒泡

阻止默认事件(表单提交,a标签跳转,右键菜单等):
return false:以对象属性的方式注册的事件才生效
e.preventDefault() (IE9以下不兼容)
e.returnValue=false (兼容IE9)

事件对象:
event(非IE浏览器都有) || window.event(用于IE浏览器)

事件源对象:
event.target (火狐只有这个)
event.srcElement (IE只有这个)
上面两种chrome都有

事件委托:
利用事件冒泡和事件源对象进行处理
优点:性能不需要循环所有的元素一个个绑定事件
      灵活,当有新的元素时不需要重新绑定事件
      
事件分类:
鼠标事件:click,mousedown,mousemove,mouseup,contextmenu,mouseover,mouseout,mouseenter,mouseleave
event中的button属性可区分鼠标按键(左键为0,右键为2)

键盘事件:keydown,keyup,keypress(keydown > keypress > keyup)
keydown和keypress的区别:keydown可响应任意键盘按键
                        keypress只响应字符类键盘按键,可返回ASCII码并转换成相应字符
                        
文本操作事件:input(实时获取文本的值),focus,blur,change(失焦后才获取文本的值)

窗体操作类(window上的事件):scroll,load

JSON

一种传输数据的格式(以对象为样板,本质上是对象,但用途有区别,对象就是本地用的,json是用来传输的)
JSON中,属性名必须带双引号
JSON.parse():string -> json
JSON.stringfy():json -> string

Load js asynchronously

js加载的缺点:加载工具方法没必要阻塞文档,使得js加载会影响页面效率,一旦网速不好,那么整个网站将等待js加载而不进行后续渲染等工作

js异步加载的三种方案:
1. defer异步加载,等到dom文档全部解析完才会被执行。只有IE能用,也可将代码写到内部。
2. async异步加载,加载完就执行,async只能加载外部脚本,不能把js写在script标签里
3. 创建script,插入到DOM中,加载完毕后callback
(其中第一和第二个方法执行时也不阻塞页面)

js loading timeline

1. 创建Document对象,开始解析web页面。解析HTML元素和他们的文本内容后添加Element对象和Text节点到文档中。 (document.readyState='loading')
2. 遇到link外部css,创建线程加载,并继续解析文档
3. 遇到script外部js,并且没有设置async,defer,浏览器加载,并阻塞,等待js加载完成并执行该脚本,然后继续解析文档
4. 遇到script外部js,并且设置有async,defer,浏览器创建线程加载,并继续解析文档。对于async属性的脚本,脚本加载完成后立即执行。(异步禁止使用document.write())
5. 遇到img等,先正常解析dom结构,然后浏览器异步加载src,并继续解析文档
6. 当文档解析完成,document.readyState='interactive'
7. 文档解析完成后,所有设置有defer的脚本会按照顺序执行
8. document对象触发DOMContentLoaded事件,标志着程序执行从同步脚本执行阶段,转化为事件驱动阶段
9. 当所有async的脚本加载完成并执行后,img等加载完成后,document.readyState='complete',window对象触发load

Regular expression

转义字符:'\'
/^开头,结尾$/
正则表达式作用:匹配特殊字符或有特殊搭配原则的字符的最佳选择

两种创建方式:直接量(推荐):var reg=/.../(后面可加i(忽略大小写)/g(全局匹配)/m(多行匹配))
             new RegExp():var reg=new RegExp('...','i/g/m')
             
正则表达式上的方法:reg.test(str)(检索字符串中指定的值。返回 true 或 false)
                   reg.exec(str)(检索字符串中指定的值。返回找到的值,并确定其位置)
                   ...
                   
支持正则表达式的string对象的方法:str.match(reg)(在字符串内检索指定的值,或找到一个或多个正则表达式的匹配)
                                str.search(reg)(检索与正则表达式相匹配的值。返回str中第一个与 reg相匹配的子串的起始位置,若没有找到任何匹配的子串,则返-1)
                                str.replace(reg/str,replacement)(用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串)
                                ...

正则表达式中一些元字符对应的表达式:\w===[0-9A-z_],\W===[^\w]
                                  \d===[0-9],\D===[^\d]
                                  \s===[\t\n\r\v\f],\S===[^\s]
                                  \b===单词边界,\B===非单词边界
                                  .===[^\r\n]
                                  
正则表达式的一些量词:n+(出现一次到多次)
                     n*(出现0次到多次)
                     n?(出现0到1次)
                     n{x}(出现x次)
                     n{x,y}(出现x到y次)
                     以上量词遵循贪婪匹配原则(尽可能匹配多的)
 
正向预查(从左到右进行匹配):?=xxx(匹配后面跟着xxx的东西)
                            ?!xxx(匹配后面不跟着xxx的东西)
负向预查(从右到左进行匹配):?<=xxx(匹配紧跟xxx后面的东西)
                            ?<!xxx(不匹配紧跟xxx后面的东西)
                     
e.g.1 检验一个字符串首尾是否含有数字
        var reg=/^\d|\d$/g;
        var str='123abc';
        reg.test(str) -> true
        
e.g.2 检验一个字符串首尾是否都含有数字
        var reg=/^\d[\s\S]*\d$/g;
        var str='123abc123';
        reg.test(str) -> true
 
 e.g.3 输出以下replace后的结果
        var str='aa'; //var reg=/a/;
        console.log(str.replace('a'/reg,'b'))
        结果为ba(一般情况下只匹配到一个)
        
        var reg=/a/g;
        console.log(str.replace(reg,'b'))
        结果为bb(全局匹配可匹配多个)
    
 e.g.4 把aabb转成bbaa
        var reg=/(\w)\1(\w)\2/g;
        var str='aabb';
        console.log(str.replace(reg,'$2$2$1$1'));
        或:console.log(str.replace(reg,function($,$1,$2){
                return $2+$2+$1+$1
            }));
 
 e.g.5 把一串数字用科学计数法表示(从右往左,每三个数打一个点)
        var str='10000000000';
        var reg=/(?=(\B)(\d{3})+$)/g;
        str.replace(reg,'.');
 
详情可参考网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp
               或https://www.w3school.com.cn/jsref/jsref_obj_regexp.asp

Some offal

一旦经历了var的操作,所得出的window上的属性,这种属性叫做不可配置的属性,delete不掉
undefined和null不与数字比较
typeof(arguments):object
document.documentElement等于html
在正则表达式中,加上?可打破贪婪匹配

Guess you like

Origin blog.csdn.net/weixin_43842821/article/details/114412071