js红宝书分享

一、事件

1、事件流

(1)冒泡(IE)
(2)捕获(Netscape)
(3)DOM2事件流

2、事件处理

(1)html事件处理

第一种:直接在html中定义事件处理程序及包含的动作。

<input type="button" value="click me!" onclick="alert('clicked!')"/>

第二种:html中定义事件处理程序,执行的动作则调用其他地方定义的脚本。

<input type="button" value="click me!" onclick="showMessage()"/>
<script>
function showMessage(){
    alert("clicked!");
}
</script>

(2)DOM0事件处理

将一个函数赋值给一个事件处理程序属性

<input id="myBtn" type="button" value="click me!"/>
<script>
    var myBtn=document.getElementById("myBtn");
    myBtn.onclick=function(){
        alert("clicked!");
    }
    
    // 删除
    myBtn.onclick = null
</script>

(3)DOM2事件处理

定义了2个方法:addEventListener()和removeEventListener()

3个参数:事件名、事件处理函数、布尔值(true表示捕获阶段调用事件处理函数,false表示冒泡阶段)

举个栗子:

<script type="text/javascript">
window.onload=function(){
    var outer = document.getElementById("outer");
    var inner = document.getElementById('inner');
    var oBox=document.getElementById('box');
    
    oBox.addEventListener("click",function(){
        alert('1');
    },true)
    oBox.addEventListener("click",function(){
        alert('4');
    },false)
    

    outer.addEventListener("click", function(){
      alert("2");
    }, true);

    
    inner.addEventListener("click",function(){
        alert('3.2')
    },false)
    

    inner.addEventListener("click", function(){
      alert("3.1");
     }, true);
    
}
</script>

<body>
    <div id="box">
        <div id="outer">
            <div id="inner"></div>
        </div>
    </div>
</body>

点击inner,弹出顺序?

??????

??????

??????

1----2----3.2-----3.1---4

原因:box上的捕获事件最先执行,然后是outer上的捕获事件,然后是inner上先注册的事件,然后是inner上后注册的事件,最后是box上的冒泡事件

addEventListener()添加的时间只能通过removeEventListener()删除,addEventListener()添加的匿名函数无法移除

相关博客戳这里

(4)IE事件处理(IE浏览器和opera浏览器支持)

attachEvent()和detachEvent()

接受2个参数:事件名称、事件处理函数

ie8及以前只支持冒泡,所以事件处理程序都在冒泡阶段

注意:

1、IE中用attachEvent和DOM0的区别,前者的this指向全局作用域,后者指向其所属元素的作用域

2、attachEvent可以为同一元素添加多个事件处理程序,是以相反的顺序被触发

(5)跨浏览器事件处理

先判断是否有DOM2级,再判断IE,最后是DOM0

 var eventUtil = {
      //添加句柄
      addHandler:function(element,type,handler){
            if (element.addEventListener){              //DOM2级
                  element.addEventListener(type,handler,false);
            }else if (element.attachEvent){             //IE
                  element.attachEvent('on' + type, handler);
            }else {                                    //DOM0级
                  element['on' + type] = handler;
            }
      },
      //删除句柄
      removeHandler:function(element,type,handler){
            if (element.removeEventListener){
                  element.removeEventListener(type,handler,false);
            }else if (element.detachEvent){
                  element.detachEvent('on' + type, handler);
            }else {
                  element['on' + type] = null;
            }
      },
      getEvent:function(event) {
            //IE678中用window.event
            return event? event: window.event;
      },
      getType:function(event) {
            return event.type;
      },
      getElement:function(event){
            return event.target || event.srcElement;
      },
      preventDefault :function(event){
            if(event.preventDefault) {
                    event.preventDefault();
            }else {  // 兼容IE
                    event.returnValue = false;
            }
      },
      stopPropagation: function(event){
            if(event.stopPropagation){
                    event.stopPropagation();
            }else {  // 兼容IE
                    event.cancelBubble = true;
            }
      }
}

3、事件对象

(1)DOM事件对象

参考p355

(2)IE中事件对象

type,srcElement,returnValue,cancelBubble

(3)跨浏览器的事件对象

见(2.3 跨浏览器事件处理)

4、事件类型(p362自己看)

(1)UI事件

(2)焦点事件

(3)鼠标、滚轮事件

(4)文本、键盘事件

(5)合成时间

(6)变动事件

(7)h5事件

(8)设备事件

5、内存和性能

(1)事件委托

原理:红宝书原话---利用冒泡,指定一个事件处理程序,管理某一类型所有事件

个人理解:利用冒泡,将子元素触发的事件写到父元素上,再通过事件对象(如event.target等)判断该做何处理。

优点:减少与dom交互,提高性能

参考博客戳这里

(2)移除事件

移除带有事件的元素时,事件仍然与按钮保持引用关系。

在某些浏览器中,事件滞留在内存中,来回切换页面时,内存中滞留的对象数增加,因为内存没被释放。

二、JSON

JSON是一种数据格式,不支持变量、函数、对象实例
JSON中,字符串必须使用双引号,对象属性加双引号,末尾不能有分号

1、语法

(1)简单值:字符串、数、布尔、null(不支持undefined)

(2)对象

(3)数组

1、解析与序列化

(1)JSON.stringify()

a)过滤

第二个参数是数组

JSON.stringify(book, ["title", "author"])
// 过滤出book的title和author属性,返回的字符串只包含这俩属性以及他们对应的值

第二个参数是函数

var book = {
    "title": "Jack",
    "authors":[
         "Nicholas"
    ],
    "edition":3,
    "year":2018
}

var jsonText = JSON.stringify(book, function(key, value){
  switch(key) {
      case "authors":
           return value.join(',')
      case "year":
           return 5000
      default:
           return value
})
b)字符串缩进

stringify有第三个参数
自己看P567

c)toJSON
var book = {
    "title": "Jack",
    "authors":[
         "Nicholas"
    ],
    "edition":3,
    "year":2018
      toJSON:function(){
            \\\\\\\
      }
}

stringify顺序:

1、如果有toJSON且能取得有效值,先调用它;

2、如果stringify有第二个参数,用过滤器;

3、对2中的值进行序列化

4、如果有第三个参数,格式化

(2)JSON.parse()

还原函数

自己看p569

三、Ajax和Comet

Ajax:无需刷新页面从服务器获取数据的一种方法

1、XMLHttpRequest

(1)用法

a)启动一个请求以备发送
 xhr.open("方法","URL","是否异步")
b)发送
 send("要作为请求主体发送的数据,无数据则传入null")
c)响应数据(自动填充xhr)
responseText: 返回的文本
responseXML:如果响应内容类型是xml/text或application/xml,则是响应数据的XML DOM,否则为null
status:响应的http状态(200~300及304为成功)
statusText:HTTP状态的说明

异步:
在判断status之前,判断xhr.readyState值,4表示完成,接收到全部响应数据;

xhr.abort()在收到响应之前可以取消异步请求

(2)HTTP头

头部信息:Accept,Accept-Charset,Accept-Encoding,Accept-Language,Connection,Cookie,Host, Referer,User-Agent

xhr.setRequestHeader()可以设置自定义的头部信息,在open之后,send之前

(3)GET

注意查询字符串的格式

参考P576

(4)POST

P577自己看

get和post的区别

2、跨域

同源策略:同域、同端口、同协议

CORS(Cross-Origin Resourse Sharing)

(1)额外Origin头

(2)服务器在Access-Control-Allow-Origin中返回相同的源信息

(1)IE对CORS的实现:引入XDR

P582自己看

(2)其他浏览器对CORS的实现:通过XMLHttpRequest实现

open中传入绝对URL

(3)跨浏览器的CORS

P585自己看

3、其他跨域技术

图像、JSONP、Comet

前两者利用img、script标签可以跨域,只能实现get

Comet是一种服务器向页面推送数据的技术(几乎实时)

两种实现comet方法:

1、长轮询:页面发送一个到服务器的请求,连接一直保持,直到有数据可以发送

2、http流:浏览器发送一个请求,连接一直保持,服务器周期性发送数据

p586自己看

相关博客戳跨域大全

3、Web Socket(新协议)

传递少量数据,比http开销小,适合移动应用,可跨域,只能发送纯文本数据

p592自己看

4、安全

CSRF(Cross-Site Request Forgery),跨站点请求伪造

(1)SSL连接

(2)每次请求都要附带经过相应算法计算的验证码

四、高级技巧

1、高级函数

(1)类型检测

var is Array = value instanceof Array
// 要返回true,value必须是一个数组,且必须与Array构造函数在同个全局作用域

利用[object NativeConstructorName]

function isArray(value){
     return Object.prototype.toString.call(value) == "[object Array]"
}

还可以用来检测Function、RegExp、原生JSON

(2)作用域安全的构造函数

function Person(name, age, job){
    this.name = name;
    this.age = age;
    this.job = job;
}
var person = new Person("Nicholas", 29, "Software Engineer");

var person = Person("Nicholas", 29, "Software Engineer");
alert(window.name); //"Nicholas"
alert(window.age); //29
alert(window.job);//"Software Engineer"

使用new操作符时,this指向新对象的实例;没使用new时,this会映射到全局window上

改写:

function Person(name, age, job){
        if (this instanceof Person){
            this.name = name;
            this.age = age;
            this.job = job;
        } else {
            return new Person(name, age, job);
        }
}

注:使用构造函数窃取模式的继承且不用原型链,这个继承可能无效

function Polygon(sides){
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function(){
        return 0; };
    } else {
        return new Polygon(sides);
    }
 }
function Rectangle(width, height){
    Polygon.call(this, 2);
    this.width = width;
    this.height = height;
    this.getArea = function(){
        return this.width * this.height;
    };
}
var rect = new Rectangle(5, 10);
alert(rect.sides);        //undefined

原因:new一个Rectangle时, Polygon.call(this, 2)中的this不是Polygon的实例,所以会在Polygon中new一个Polygon

解决办法:加上原型链

function Polygon(sides){
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function(){
        return 0; };
    } else {
        return new Polygon(sides);
    }
 }
function Rectangle(width, height){
    Polygon.call(this, 2);
    this.width = width;
    this.height = height;
    this.getArea = function(){
        return this.width * this.height;
    };
}
Rectangle.prototype = new Polygon();
var rect = new Rectangle(5, 10);
alert(rect.sides);        //undefined

原因:一个Rectangle实例也是一个Polygon实例,所以new 一个Rectangle中,会把Rectangle中的this传给Polygon

(3)惰性载入函数

目的:减少判断语句的执行次数

function createXHR(){
        if (typeof XMLHttpRequest != "undefined"){
            return new XMLHttpRequest();
        } else if (typeof ActiveXObject != "undefined"){
            if (typeof arguments.callee.activeXString != "string"){
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],
                                i,len;
                for (i=0,len=versions.length; i < len; i++){
                     try {
                         new ActiveXObject(versions[i]);
                         arguments.callee.activeXString = versions[i];
                          break;
                     } catch (ex){
                        //跳过
                     }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        } else {
            throw new Error("No XHR object available.");
        }
}

方法(1)每个判断分支都定义一个函数覆盖原有函数,这样第一次调用会进行判断,之后调用就直接使用某个分支里的函数

function createXHR(){
      if (typeof XMLHttpRequest != "undefined"){
            createXHR = function(){
                return new XMLHttpRequest();
            }; 
      } else if (typeof ActiveXObject != "undefined"){
            createXHR = function(){
                if (typeof arguments.callee.activeXString != "string"){
                    var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],
                    for (i=0,len=versions.length; i < len; i++){
                        try {
                          new ActiveXObject(versions[i]);
                          arguments.callee.activeXString = versions[i];
                             break;
                        } catch (ex){
                            //skip }
                        }
                 }
                 return new ActiveXObject(arguments.callee.activeXString);
             };
      } else {
           createXHR = function(){
                throw new Error("No XHR object available.");
           };
      }
    return createXHR();
}
                                    

方法(2)利用var声明自执行的匿名函数,每个判断分支中返回正确的函数定义,第一次加载时要判断,调用时不用。

var createXHR = (function(){
    if (typeof XMLHttpRequest != "undefined"){
        return funtion(){
            return new XMLHttpRequest();
        }
    } else if (typeof ActiveXObject != "undefined"){
        return function(){
            if (typeof arguments.callee.activeXString != "string"){
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],
                                i, len;
                for (i=0,len=versions.length; i < len; i++){
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch (ex){
                        //skip 
                    }
                }
           }
           return new ActiveXObject(arguments.callee.activeXString);
        }
    }else {
        return function(){
            throw new Error("No XHR object available.")
        }
    }
})()

(4)函数绑定:修改函数的执行上下文(this)

call(context,a,b,c...),call立即执行

bind(context,a,b,c...),bind返回的是函数,调用才执行

apply(context,[a,b,c....]),apply立即执行

栗子:

Math.min(23, 34, 24, 12, 35, 36, 14, 25)


arr=[23, 34, 24, 12, 35, 36, 14, 25]
var min = Math.min.apply(null, arr);

(5)函数currying

方法:使用一个闭包返回一个函数,返回的函数还需要设置参数

function curry(fn){
        var args = Array.prototype.slice.call(arguments, 1); // 第一个参数是要柯里化的函数,所以从第二个参数开始slice,args包含了来自外部的参数
        return function(){
            var innerArgs = Array.prototype.slice.call(arguments); //存放来自内部函数的参数
            var finalArgs = args.concat(innerArgs);
            return fn.apply(null, finalArgs);// 将拼接好的参数传给fn
        };
 }

//可以分开传参
function add(num1, num2){
    return num1 + num2;
}
var curriedAdd = curry(add, 5);
alert(curriedAdd(3));   //8
//也可以一次性传参
function add(num1, num2){
    return num1 + num2;
}
var curriedAdd = curry(add, 5, 12);
alert(curriedAdd());   //17

2、防篡改对象

p606自己看

3、高级定时器

js是单线程,浏览器负责排序,指派某段代码在某个时间点运行的优先级。

js有一个主执行进程,还有一个代码队列。

给队列添加代码不代表它立即执行,只代表它会尽快执行(进程空闲时);定时器意味着隔了某段时间后代码被添加到队列,不带表某段时间后立即执行。

重复定时器和数组分块自己看p610

函数节流:某些代码不可以在没有间断的情况下连续重复执行

function throttle(method, context) {
    clearTimeout(method.tId);
    method.tId= setTimeout(function(){
        method.call(context);
    }, 100);
}

参考博客:函数节流与防抖

4、自定义事件:有助于解耦

发布-订阅模式模式

p616自己看

参考博客这里

五、最佳实践

1、可维护性

(1)(可理解、直观、可适应、可扩展、可调试)

函数和方法---注释

大段代码---注释

复杂算法---注释

变量名:避免拼音,英文应该直观清楚,合乎逻辑;变量名为名词,函数应以动词开始。

(2)松散耦合

p659自己看

(3)编程实践

1、尊重对象所有权:不是自己的对象不要乱改

2、避免全局变量

3、避免与null比较

4、使用常量:抽取出来单独定义

2、性能

(1)避免全局查找

(2)算法复杂度

*使用变量和数据比访问对象上的属性更有效率,经常用的对象属性要存在局部变量中。

优化循环:

1、--代替++,更高效(原因

2、简化终止条件

3、简化循环体

4、合理使用后测试循环如do-while

*针对大数据集,展开循环更快:duff装置

p670自己看

其他:js原生方法比自己写快、switch比复杂if-else快、位运算符较快

(3)最小语句数(能少写就少写)

1、少用几个var,合在一起写

2、++或==尽量合并语句,不要单独写

3、使用字面量创建和初始化数组

(4)优化dom交互

1、最小化现场更新:减少对dom的更新次数,使用文档片段(fragment)构建dom结构,全部创建好后使用appendChild()添加

2、innerHTML:对于大的dom,用innerHTML效率更高。
使用innerHTML设置某个值时,后台会创建一个HTML解析器,使用内部的DOM调用来创建DOM结构,而不是基于js的DOM改写,所以快很多。

3、使用事件代理:利用冒泡,将目标的事情在祖先节点上处理,这样减少dom间的交互,增强性能。

4、HTMLCollection: 带有方法的 HTML 元素的集合

以下情况会返回HTMLCollection对象

(1)调用getElementsByTagName()

(2)获取元素的childNodes属性

(3)获取元素的attributes属性

(4)访问了特殊的集合,如document.forms, document.images等

优化方法:将循环中的数组长度计算移至for循环的初始化部分

栗子1:

var images = document.getElementsByTagName("img"),
             i, len;

// 要这样
for(i=0,len=images.length; i<len; i++){
    //处理arr[i]
}
//而不是这样
for(i=0; i<images.length; i++){
}

栗子2:

// 比如这样,循环内可以减少访问images的HTMLCollection
var images = document.getElementsByTagName("img"),
             image,
             i, len;

for(i=0,len=images.length; i<len; i++){
     image = images[i]
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容