JS高级技巧学习小结

安全类型检测

<script type="text/javascript">
        //判断某个值是不是原生数组
        function isArray(value){
            return Object.prototype.toString.call(value)=="[object Array]";
        }
        //判断某个值是不是原生函数
        function isFunction(value){
            return return Object.prototype.toString.call(value)=="[object Function]";
        }
        //判断某个值是不是原生正则表达式
        function isRegExp(value){
            return return Object.prototype.toString.call(value)=="[object RegExp]";
        }
    </script>

作用域安全的构造函数

构造函数其实就是一个使用new操作符调用的函数,当使用new调用时,构造函数内部用到的this对象会指向新创建的对象实例。

Person构造函数添加了一个检查并确保this对象是Person实例的if语句,它要么使用new操作符,要么在现有的Person实例环境中调用构造函数。任何一种情况,对象初始化都可以正常进行。

如果this对象不是Person的实例,那么会再次使用new操作符调用构造函数并返回结果。这样就可以确保无论是否使用new操作符,都会返回一个Person的新实例。

Demo1:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>作用域安全的构造函数</title>
    </head>
    <body>
    <script type="text/javascript">
    function Person(name,age,job){
        if(this instanceof Person)//这里检测以确保this是Person的实例
        {
           this.name=name;
            this.age=age;
            this.job=job;
        }else{
            return new Person(name,age,job);
        }

    }
    var person1=Person("liujie",23,"master");
    console.log(window.name);//""
    console.log(person1.name);//liujie
    var person2=new Person("lisi",21,"student");
    console.log(person2.name);//lisi
    </script>
    </body>
</html>

Demo2

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>作用域安全的构造函数</title>
</head>
<body>
    <script type="text/javascript">
        function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
        }
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        console.log(person1);//[object Object]
        console.log(person1.name);     //"Nicholas"
        console.log(person1.age);      //29
        console.log(person1.job);      //"Software Engineer"

        var person2 = Person("Nicholas", 29, "Software Engineer");
        //这里忽略了new操作符,把构造函数作为普通函数调用
        console.log(person2);         //undefined  因为Person函数没有返回值
        console.log(window.name);     //"Nicholas"  这里this-->window
        console.log(window.age);      //29
        console.log(window.job);      //"Software Engineer"
    </script>
</body>
</html>

这里问题在于没有使用new操作符来调用该构造函数的情况上,由于该this对象是在运行时绑定的,所以直接调用Person(),this会映射到全局对象window上,导致错误对象属性的意外增加。

这里原本针对Person实例的三个属性被加到window对象上,因为构造函数是作为普通函数调用的,忽略了new操作符。这个问题是由于this对象的晚绑定造成的,在这里this被解析成了window对象。由于window的name属性是用于识别链接目标和frame的,所以这里对该属性的偶然覆盖可能会导致该页面上出现其他错误。可以创建一个作用域安全的构造函数来解决这个问题。

Demo3

在实现了作用域安全的构造函数后,如果使用构造函数窃取模式的继承且不使用原型链,那么这个继承可能被破坏。

下面的代码,Polygon构造函数是作用域安全的,然而Rectangle构造函数则不是。新创建一个Rectangle实例后,这个实例应该通过Polygon.call()来继承Polygon的sides属性。但是,由于Polygon构造函数是作用域安全的,this对象并非Polygon的实例,所以会创建并返回一个新的Polygon对象。Rectangle构造函数中的this对象并没有得到增长,同时Polygon.call()返回的值也没有用到,所以Rectangle实例中就不会有sides属性。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>作用域安全的构造函数</title>
</head>
<body>
    <script type="text/javascript">
         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);
        console.log(rect.sides);   //undefined
    </script>
</body>
</html>

Demo4

构造函数窃取结合使用原型链可以解决这个问题

这样一来,一个Rectangle实例也同时是一个Polygon实例,所以Polygon.call()会照原意执行,最终为Rectangle实例添加sides属性。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>作用域安全的构造函数</title>
</head>
<body>
    <script type="text/javascript">
         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);
        console.log(rect.sides);   //2
    </script>
</body>
</html>

惰性载入函数

惰性载入表示函数执行的分支仅会发生一次。

有两种实现惰性载入的方式,第一种就是在函数被调用时再处理函数。在第一次调用的过程中,该函数会被覆盖为另一个按合适方式执行的函数,这样任何对原函数的调用都不用再经过执行的分支了。

在这个惰性载入的createXHR()中,if语句的每一个分支都会createXHR变量赋值,有效覆盖了原有的函数。最后一步便是调用新赋的函数。下一次调用createXHR()的时候,就会直接调用被分配的函数,这样就不需要再次执行if语句了。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>惰性载入函数</title>
</head>
<body>
<script type="text/javascript">
        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"],
                            i, len;

                        for (i=0,len=versions.length; i < len; i++){
                            try {
                                new ActiveXObject(versions[i]);
                                arguments.callee.activeXString = versions[i];
                            } catch (ex){
                                //skip
                            }
                        }
                    }
                    return new ActiveXObject(arguments.callee.activeXString);
                };
            } else {
                createXHR = function(){
                    throw new Error("No XHR object available.");
                };
            }
            return createXHR();
        }
        var xhr1 = createXHR();
        var xhr2 = createXHR();
    </script>
</body>
</html>

第二种实现方式:在声明函数时就指定适当的函数。

这样,第一次调用函数时就不会损失性能了,而在代码首次加载时会损失一点性能。

这个例子的技巧:创建了一个匿名、自执行的函数,用以确定应该使用哪一个函数实现。每个分支都返回正确的函数定义,以便立即将其赋值给createXHR()

惰性载入函数的优点是只在执行分支代码时牺牲一点性能。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>惰性载入函数</title>
</head>
<body>
<script type="text/javascript">
        var createXHR = (function(){
            if (typeof XMLHttpRequest != "undefined"){
                return function(){
                    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.");
                };
            }
        })();
        var xhr1 = createXHR();
        var xhr2 = createXHR();
    </script>
</body>
</html>

函数绑定

函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。

js库实现了一个可以将函数绑定到指定环境的函数–bind()

bind()函数接收一个函数和一个环境,并返回一个在给定环境中调用给定函数的函数,并且将所有参数原封不动传递过去。

在bind()函数中创建了一个闭包,闭包使用apply()调用传入的函数,并给apply()传递context对象和参数。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>函数绑定</title>
</head>
<body>
<input type="button" id="my-btn" value="Click Me" />
<script type="text/javascript" src="EventUtil.js"></script>
    <script type="text/javascript">
        function bind(fn, context){//接收一个函数和一个环境
            return function(){
                return fn.apply(context, arguments);
            };
        }
        var handler = {
            message: "Event handled",
            handleClick: function(event){
                console.log(this.message + ":" + event.type);//Event handled:click
            }
        };
        var btn = document.getElementById("my-btn");
        EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler));
    </script>
</body>
</html>

函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。

下面的例子将对象handler的handleClick方法分配为按钮的事件处理程序。当按下按钮时,就应该调用该函数,显示一个警告框。虽然貌似警告框应该显示Event handled,然而实际上显示undefined。这是因为没有保存handler.handleClick()的执行环境,所以this指向了DOM按钮而不是handler对象。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>函数绑定</title>
</head>
<body>
<input type="button" id="my-btn" value="Click Me" />
<script type="text/javascript" src="EventUtil.js"></script>
    <script type="text/javascript">
        var handler = {
            message: "Event handled",
            handleClick: function(event){
                //console.log(this);//<input id="my-btn" type="button" value="Click Me">
                console.log(this.message);//undefined
            }
        };
        var btn = document.getElementById("my-btn");
        EventUtil.addHandler(btn, "click", handler.handleClick);
    </script>
</body>
</html>

函数绑定要创建一个函数,可以在特定的this环境中以指定参数调用另一个函数。该技巧常常和回调函数与事件处理程序一起使用,以便在将函数作为变量传递的同时保留代码执行环境。

下面的例子将对象handler的handleClick方法分配为按钮的事件处理程序。当按下按钮时,就应该调用该函数,显示一个警告框。虽然貌似警告框应该显示Event handled,然而实际上显示undefined。这是因为没有保存handler.handleClick()的执行环境,所以this指向了DOM按钮而不是handler对象。

这里在onclick事件处理程序中使用了一个闭包直接调用handler.handleClick()。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>函数绑定</title>
</head>
<body>
<input type="button" id="my-btn" value="Click Me" />
<script type="text/javascript" src="EventUtil.js"></script>
    <script type="text/javascript">
        var handler = {
            message: "Event handled",
            handleClick: function(event){
                //console.log(this);// Object { message="Event handled",  handleClick=function()}
                console.log(this.message);//Event handled
            }
        };
        var btn = document.getElementById("my-btn");
        EventUtil.addHandler(btn, "click", function(event){
            handler.handleClick(event);
        });
    </script>
</body>
</html>

ECMAScript5为所有函数定义了一个原生的bind()方法,进一步简单了操作。

不管原生的bind方法还是自定义的bind方法,都需要传入作为this值的对象

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>bind函数绑定</title>
</head>
<body>
<input type="button" id="my-btn" value="Click Me" />
<script type="text/javascript" src="EventUtil.js"></script>
    <script type="text/javascript">
        var handler = {
            message: "Event handled",
            handleClick: function(event){
                console.log(this.message + ":" + event.type);
            }
        };
        var btn = document.getElementById("my-btn");
        EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler));
    </script>
</body>
</html>

函数柯里化

函数柯里化–用于创建已经设置好了一个或多个参数的函数。其基本方法与函数绑定一样:使用一个闭包返回一个函数。两者区别在于:函数柯里化在函数被调用时,返回的函数还需要传入参数。

创建方法:调用另一个函数并为它传入要柯里化的函数和必要参数

Demo1

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>函数柯里化</title>
</head>
<body>
    <script type="text/javascript">
        function curry(fn){
            //这里在arguments对象上调用了slice()方法,并传入参数1表示被返回的数组包含从第二个参数开始的所有参数
            var args = Array.prototype.slice.call(arguments, 1);//slice() 方法可从已有的数组中返回选定的元素。
            return function(){
                var innerArgs = Array.prototype.slice.call(arguments),//innerArgs表示内部函数的参数数组
                    finalArgs = args.concat(innerArgs);//将外部函数参数数组和内部函数参数数组进行连接
                    //concat()连接两个或更多的数组,并返回结果。
                return fn.apply(null, finalArgs);//这里的null表示没有考虑fn函数的执行环境
            };
        }

        function add(num1, num2){//求和函数
            return num1 + num2;
        }
        //curry()函数的第一个参数是要柯里化的函数,其他参数是要传入的值
        var curriedAdd = curry(add, 5);//这里的5是外部函数参数,3是内部函数参数
        alert(curriedAdd(3));   //8

        var curriedAdd2 = curry(add, 5, 12);//柯里化的add函数
        alert(curriedAdd2());   //17

    </script>
</body>
</html>

Demo2

函数柯里化还常常作为函数绑定的一部分包含在其中,构造出更为复杂的bind函数。

这里bind同时接受函数和一个object对象。表示给被绑定的函数的参数是从第三个开始的。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>函数柯里化</title>
</head>
<body>
    <input type="button" id="my-btn" value="Click Me">
    <script type="text/javascript" src="../EventUtil.js"></script>
    <script type="text/javascript">
        function bind(fn, context){//fn=handler.handleClick   context=handler
            var args = Array.prototype.slice.call(arguments, 2);//获取到"my-btn"
            return function(){
                var innerArgs = Array.prototype.slice.call(arguments),
                    finalArgs = args.concat(innerArgs);
                return fn.apply(context, finalArgs);//handler.handleClick.apply(handler, "my-btn");
            };
        }

        var handler = {
            message: "Event handled",
            handleClick: function(name, event){//name是要处理的元素的名字
                //event就是event对象
                console.log(this.message + ":" + name + ":" + event.type);//Event handled:my-btn:click
            }
        };

        var btn = document.getElementById("my-btn");
        EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler, "my-btn"));
    </script>
</body>
</html>

Demo3

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
    <title>函数柯里化</title>
</head>
<body>
    <input type="button" id="my-btn" value="Click Me">
    <script type="text/javascript" src="../EventUtil.js"></script>
    <script type="text/javascript">
        var handler = {
            message: "Event handled",
            handleClick: function(name, event){
                console.log(this.message + ":" + name + ":" + event.type);
            }
        };
        var btn = document.getElementById("my-btn");
        EventUtil.addHandler(btn, "click", handler.handleClick.bind(handler, "my-btn"));
        //handler.handleClick.bind(handler, "my-btn")这样绑定指handler.handleClick函数中的this指向handler对象
        //bind()方法也实现了函数柯里化,只要在this的值之后再传入另一个参数即可
    </script>
</body>
</html>

防纂改对象

不可扩展对象

<script type="text/javascript">
/*
Object.preventExtensions()用来阻止给对象添加属性和方法
 */
    var person = { name: "Nicholas" };
        Object.preventExtensions(person);

        person.age = 29;
        console.log(person.age);//undefined
</script>
<script type="text/javascript">
/*
Object.preventExtensions()用来阻止给对象添加属性和方法
Object.isExtensible()方法用来判断元素是否可以扩展
 */
    var person = { name: "Nicholas" };
        console.log(Object.isExtensible(person));   //true

        Object.preventExtensions(person);
        console.log(Object.isExtensible(person));   //false

        person.age = 29;
        console.log(person.age);//undefined
</script>

密封的对象

<script type="text/javascript">
/*
Object.seal()将对象密封,不能给对象添加和删除属性和方法
 */
    var person = { name: "Nicholas" };
        Object.seal(person);

        person.age = 29;
        console.log(person.age);      //undefined

        delete person.name;
        console.log(person.name);     //"Nicholas"
</script>
<script type="text/javascript">
/*
Object.seal()将对象密封,不能给对象添加和删除属性和方法
 */
    var person = { name: "Nicholas" };
        console.log(Object.isExtensible(person));   //true  返回true表示对象可以扩展
        console.log(Object.isSealed(person));       //false   返回false表示对象没有密封

        Object.seal(person);
        console.log(Object.isExtensible(person));   //false
        console.log(Object.isSealed(person));       //true  对象被密封

        person.age = 29;
        console.log(person.age);//undefined"
</script>

冻结的对象

<script type="text/javascript">
/*
Object.freeze()方法是冻结对象,冻结的对象既不能扩展,同时也是密封的,而且对象的[[writeable]]特性也被设置为false
 */
    var person = { name: "Nicholas" };
        Object.freeze(person);

        person.age = 29;//不可以扩展
        console.log(person.age);      //undefined

        delete person.name;//不可以删除
        console.log(person.name);     //"Nicholas"

        person.name = "Greg";//因为writeable]]特性被设置为false的原因,不能被修改
        console.log(person.name);     //"Nicholas"
</script>
<script type="text/javascript">
/*
Object.freeze()方法是冻结对象,冻结的对象既不能扩展,同时也是密封的,而且对象的[[writeable]]特性也被设置为false
 */
    var person = { name: "Nicholas" };
        console.log(Object.isExtensible(person));   //true
        console.log(Object.isSealed(person));       //false
        console.log(Object.isFrozen(person));       //false  Object.isFrozen()用来判断对象是否被冻结

        Object.freeze(person);
        console.log(Object.isExtensible(person));   //false
        console.log(Object.isSealed(person));       //true
        console.log(Object.isFrozen(person));       //true

        person.age = 29;
        console.log(person.age);//undefined
</script>

高级定时器

js是运行于单线程的环境中的,定时器仅仅只是计划代码在未来的某个时间执行。执行时机是不能保证的,因为在页面的生命周期中,不同事件可能有其他代码在控制js进程。在页面下载完后的代码运行、事件处理程序、Ajax回调函数都必须使用同样的线程来执行。实际上,浏览器负责进行排序,指派某段代码在某个时间点运行的优先级。

当某个按钮被按下,它的事件处理程序代码就会被添加到队列中,并在下一个可能的时间里执行。当接收到某个Ajax响应时,回调函数的代码会被添加到队列。在js中没有任何代码时立刻执行的,但是一旦进程空闲则尽快执行。

定时器对队列的工作方式是:当特定时间过去后将代码插入。注意,给队列添加代码并不意味着对它立刻执行,而只能表示它会尽快执行。例如:设定一个150ms后执行的定时器不代表到了150ms代码就立刻执行,它表示代码会在150ms后被加入到队列中。如果在这个时间点,队列中没有其他东西,那么这段代码就会被执行,表面上看上去就好像代码就在精确的时间点上执行了。其他情况,代码可能明显等待更长时间才执行。

Demo1:

为了避免setInterval()的重复定时器的缺点,可以采用链式setTimeout()方式

调用setTimeout(),每次函数执行的时候都会创建一个新的定时器。第二个setTimeout()调用使用了arguments.callee来获取对当前执行的函数的引用,并为其设置另外一个定时器。这样做的好处:在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何缺失的间隔。而且,它可以保证在下一次定时器代码执行之前,至少要等待指定的间隔,避免了连续的运行。这个模式主要用于重复定时器。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>重复的定时器</title>
</head>
<body>
<div id="myDiv" style="position:absolute;width:100px;height:100px;left:0px;top:10px;background:red;"></div>
    <script type="text/javascript">
        setTimeout(function()
        {
           var div = document.getElementById("myDiv"),
               left = parseInt(div.style.left) + 5;
              div.style.left = left + "px";

           if (left < 200){
               setTimeout(arguments.callee, 50);
           }

        }, 50);
    </script>
</body>
</html>

数组分块技术

数组分块技术基本的思路:为要处理的项目创建一个队列,然后使用定时器取出下一个要处理的项目进行处理,接着再设置另一个定时器。

数组分块的重要性在于它可以将多个项目的处理在执行队列上分开,在每个项目处理之后,给予其他的浏览器处理机会运行,这样就可能避免长时间运行脚本的错误。

data.concat():当不传递任何参数调用数组的concat()方法时,将返回和原来数组中项目一样的数组。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>数组分块技术</title>
</head>
<body>
<div id="myDiv" style="background:red;"></div>
    <script type="text/javascript">
        var data = [12,123,1234,453,436,23,23,5,4123,45,346,5634,2234,345,342];
        function chunk(array, process, context){
        //三个参数:要处理的项目的数组,用于处理项目的函数,可选的运行该函数的环境
            setTimeout(function(){
                var item = array.shift();//获取队列中下一个要处理的项目
                process.call(context, item);

                if (array.length > 0){
                    setTimeout(arguments.callee, 100);
                }
            }, 100);
        }
        function printValue(item){
            var div = document.getElementById("myDiv");
            div.innerHTML += item + "<br>";
        }
        chunk(data, printValue);
    </script>
</body>
</html>

函数节流

函数节流背后的基本思想是:某些代码不可以在没有间断的情况下连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除之前的定时器并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意义。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的是在只有在执行函数的请求停止了一段时间之后才执行。

我们使用throttle()函数来实现定时器的设置和清除。

throttle()函数接收两个参数:要执行的函数以及在哪个作用域中执行。在函数中先清除之前设置的任何定时器。定时器ID是存储在函数的tId属性中的。

只要代码时周期性执行的,都应该使用节流,但是你不能控制请求执行的速率。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>函数节流</title>
</head>
<body>
    <div id="myDiv" style="background:red;"></div>
    <script type="text/javascript">
        function throttle(method, scope) {
            clearTimeout(method.tId);
            method.tId= setTimeout(function(){
                method.call(scope);
            }, 100);
        }
        function resizeDiv(){
            var div = document.getElementById("myDiv");
            div.style.height = div.offsetWidth + "px";
        }

        window.onresize = function(){
            throttle(resizeDiv);
        };
    </script>
</body>
</html>

自定义事件

事件是一种叫做观察者的设计模式,这是一种创建松散耦合代码的技术,对象可以发布事件,用来表示在该对象生命周期中某个时刻到了,然后其他对象可以观察该对象,等待这些时刻到来并通过运行代码来响应。

观察者模式由两类对象组成:主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即使观察者不存在。

Demo1

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>自定义事件</title>
</head>
<body>
<div id="myDiv" style="background:red;"></div>
    <script type="text/javascript">
        function handleMessage(event){
            console.log("Message received: " + event.message);//Hello world!
        }
        //创建一个新对象
        var target = new EventTarget();
        //添加一个事件处理程序
        target.addHandler("message", handleMessage);
        //触发事件
        target.fire({ type: "message", message: "Hello world!"});
        //移除事件处理程序
        target.removeHandler("message", handleMessage);
        //再次触发,但不会显示任何警告框
        target.fire({ type: "message", message: "Hello world!"});
    </script>
</body>
</html>

事件是一种叫做观察者的设计模式,这是一种创建松散耦合代码的技术,对象可以发布事件,用来表示在该对象生命周期中某个时刻到了,然后其他对象可以观察该对象,等待这些时刻到来并通过运行代码来响应。

观察者模式由两类对象组成:主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即使观察者不存在。

Person类型使用寄生组合继承方法来继承EventTarget

Demo2

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>自定义事件</title>
</head>
<body>
<div id="myDiv" style="background:red;"></div>
    <script type="text/javascript">
        function object(o){
            function F(){}
            F.prototype = o;
            return new F();
        }

        function inheritPrototype(subType, superType){
            var prototype = object(superType.prototype);   //创建对象
            prototype.constructor = subType;               //增强对象
            subType.prototype = prototype;                 //指定对象
        }

        function Person(name, age){
            EventTarget.call(this);
            this.name = name;
            this.age = age;
        }

        inheritPrototype(Person,EventTarget);
        Person.prototype.say = function(message){
            this.fire({type: "message", message: message});
        };
        function handleMessage(event){
            console.log(event.target.name + " says: " + event.message);
        }
        var person = new Person("Nicholas", 29);
        person.addHandler("message", handleMessage);
        person.say("Hi there.");
    </script>
</body>
</html>
时间: 2024-08-05 22:56:13

JS高级技巧学习小结的相关文章

javascript 学习小结 JS装逼技巧(一) by FungLeo

javascript 学习小结 JS装逼技巧(一) by FungLeo 前言 最近一直在做javascript方面的工作.但是本身我的javascript水平比较低,因此在学习过程中比较困难.而最近又接触到了很多的知识点.好记性不如烂笔头,因此写这篇零碎的博文,记一记我学到的一些好玩的东西. 简单的新建各种元素 创建各种元素都有相对应的方法,例如,创建一个数组可以这样写var arr = new Array 当然,这样做是对的,但是我英文很烂,并且不喜欢这样的代码.我喜欢的是下面这样的. //

《Pro AngularJS》学习小结-01

<Pro AngularJS>该书以一个SportsStore案例为主线铺开. 一.开发环境设置 该书中所用的server开发环境是Deployed,从来没听说过,而且作者也说该server没什么人用,我干脆弃用之.其他的环境包括 NodeJS--这个必须装 karma--测试环境,前期还没有用到,以后认真研究,毕竟AngularJS一大特点是Unit Test bootstrap--这个现在应该普遍使用了,O(∩_∩)O webstorm--现在唯一支持AngularJS插件的IDE 我基本

自动化测试Selenium Webdriver (JAVA)学习小结

自动化测试--Selenium学习小结 一.自动化测试的概念及意义: 1.什么是自动化测试: 一般是指软件测试的自动化,软件测试就是在预设条件下运行系统或应用程序,评估运行结果,预先条件应包括正常条件和异常条件. 2.意义: 让测试更有效率,利用更多的空余时间,减少人力资源. 二.selenium工具 我用的是java语言,所以接下来的例子和方法都是基于java的. 1.环境配置 (1)Jdk的配置: 我用的是1.7的jdk,配置方法都一样,新建一个JAVA_HOME,把你装好的jdk的路径复制

javascript 学习小结 (二) by FungLeo

javascript 学习小结 (二) by FungLeo 前言 前面写过一个学习小结javascript 学习小结 JS装逼技巧(一) by FungLeo 那篇博文总结的东西还是比较多的. 但是JS有很多的内容,都是很有用的知识点,不可能一下子记住.因此,我的学习小结的会一直更新. 因为学习进度的不同,可能每篇博文的长短也不一样,有的学的东西多,就长点. 查询某个字符串在某个数组中的索引值 笨方法 我的基础比较差,所以很多东西是记不住的.因此,我在需要这样做的时候,我写了如下代码 var

varnish学习小结

大纲 一.Varnish 简介 二.Varnish 特点 三.Varnish 与 Squid 对比 四.Varnish 设计结构 五.Varnish 工作流程 六.Varnish 状态引擎(state engine) 七.安装与配置 Varnish 一.Varnish 简介 Varnish是一款高性能的开源HTTP加速器,挪威最大的在线报纸 Verdens Gang 使用3台Varnish代替了原来的12台Squid,性能比以前更好. Varnish 的作者Poul-Henning Kamp是F

HTML5学习小结

HTML5是用于取代1999年所制定的 HTML4.01和XHTML1.0标准的HTML标准版本.HTML5的第一份正式草案已于2008年1月公布:2012年12月,规范已经正式定稿.W3C计划在2014年底发布HTML5推荐标准,在2016年底发布HTML5.1推荐标准.HTML5有两大特点:强化了Web网页的表现性能:追加了本地数据库等功能. HTML5向前兼容,只去除很少的部分,比如<frame><font>等.HTML5面向移动,支持IP,GPS,WIFI MAC,GSM/

javascript 学习小结 (三) jQuery封装ajax尝试 by FungLeo

javascript 学习小结 (三) jQuery封装ajax尝试 by FungLeo 前言 在JS学习中,对于原生的很多东西我理解得并不透彻.但是使用jQuery来操作DOM,基本上还是非常熟练的.但是对于AJAX数据交互的处理,我不是很理解. 近期团队交给我一个后端全接口提供给我的项目.我要利用这些接口来自己组织前端代码.为了学习,我决定不使用VUE或者其他的前端框架来做.而是只使用jQuery框架,数据的部分全部使用拼接字符串的形式实现. 获取数据,显示数据,提交数据. 在这个项目中(

ExtJs学习笔记之学习小结LoginDemo

ExtJs学习小结LoginDemo 1.示例:(登录界面) <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <link rel="stylesheet" type="text/css" href="../ext-js-4.2.1/res

JS入门学习,写一个时钟~

<!-- 耽搁了几天,于是又继续回到JS的学习了~~ 各种头大,加油吧... --> <!doctype html><html><head> <title>数码时钟</title> <meta charset="gb2312"> <style type="text/css"> *{ padding:0; margin:0;} body{background:rgb(0,2