晴明的博客园 GitHub      CodePen      CodeWars     

[js] arguments、call、apply、bind

#call.bind

            var slice1 = Array.prototype.slice;
            console.log(slice1.apply('abef'));//["a", "b", "e", "f"]
            
            var unboundSlice = Array.prototype.slice;
            var slice2 = Function.prototype.call.bind(unboundSlice);
            console.log(slice2('abef'));//["a", "b", "e", "f"]

#bind.apply

            var make_lazy = function(fn) {
                return fn.bind.apply(fn, arguments); //??? 
            };
            
            //类似
            var make_lazy = function(fn) {
                var orgBindFun = Function.prototype.bind;
                return orgBindFun.apply(fn, arguments);
                //return fn.bind(fn, arguments0, arguments1, arguments2....);//类似这样,将参数拆开
            };

 

arguments

arguments 是一个类数组对象。代表传给一个function的参数列表。

            function myConcat(separator) {
                
                var args = Array.prototype.slice.call(arguments, 1);
                console.log(arguments);//数组?
                console.log(arguments.length);
                console.log(arguments.callee);//方法本身
                console.log(arguments.callee.name);//方法名
                console.log(args);
                return args.join(separator);
            }
            // returns "red, orange, blue"
            myConcat(", ", "red", "orange", "blue");
            // returns "elephant; giraffe; lion; cheetah"
            myConcat("; ", "elephant", "giraffe", "lion", "cheetah");
            // returns "sage. basil. oregano. pepper. parsley"
            myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

 

 

Function.prototype.call()

   fun.call(thisArg[, arg1[, arg2[, ...]]])

有点像  父call(子,传入父的参数)

call和apply是为了动态改变this而出现的,当一个object没有某个方法,但是其他的有,我们可以借助call或apply用其它对象的方法来操作。

thisArg:

可选。将作为当前对象使用的对象。

是你想指定的上下文,他可以任何一个 JavaScript 对象(JavaScript 中一切皆对象)。
在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,

如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(window),

同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

//作用和 apply() 方法类似,只有一个区别,
    //就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
    //通过 call 方法,你可以在一个对象上借用另一个对象上的方法

 

#动态的改变this

            function add(a, b) {
                console.log(a + b);
                console.dir(this);
            }

            function sub(a, b) {
                console.log(a - b);
                console.dir(this);
            }
            add.call(sub, 3, 1); //4  sub(a, b)
            sub.call(add, 3, 1); //2  add(a, b)

#

            function Animal() {
                this.name = "Animal";
                this.showName = function() {
                    console.log(this.name);
                }
            }

            function Cat() {
                this.name = "Cat";
            }
            var animal = new Animal();
            var cat = new Cat();
            //通过call或apply方法,将原本属于Animal对象的showName()方法交给对象cat来使用了。    
            //输出结果为"Cat"    
            animal.showName.call(cat, ",");
            //animal.showName.apply(cat,[]);

 

#

var zhangfei = {
    father: {
        name: '你管得着么'
    }
};
var guanyu = {
    father: {
        name: function() {
            return '你猜,你猜,你猜猜猜'
        }
    }
};
[zhangfei, guanyu].map(function(who) {
    var name = fatherName.call(who);
    return Object.prototype.toString.call(name) === "[object Function]" ? name() : name;
}); //["你管得着么", "你猜,你猜,你猜猜猜"]


#调用父构造函数的 call 方法来实现继承

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>

    <body>
        <script>
            function Product(name, price) {
                this.name = name;
                this.price = price;
                if (price < 0) {
                    throw RangeError('Cannot create product ' +
                        this.name + ' with a negative price');
                }
                return this;
            }

            function Food(name, price) {
                Product.call(this, name, price);
                this.category = 'food';
            }
            Food.prototype = Object.create(Product.prototype);
            Food.prototype.constructor = Food; // Reset the constructor from Product to Food
            function Toy(name, price) {
                Product.call(this, name, price);
                this.category = 'toy';
            }
            Toy.prototype = Object.create(Product.prototype);
            Toy.prototype.constructor = Toy; // Reset the constructor from Product to Toy
            var cheese = new Food('feta', 5);
            console.log(cheese);
            var fun = new Toy('robot', 40);
            console.log(fun);
        </script>
    </body>

</html>

 

使用call方法调用匿名函数并且指定其的'this'的位置

 

            var animals = [{
                species: 'Lion',
                name: 'King'
            }, {
                species: 'Whale',
                name: 'Fail'
            }];
            for (var i = 0; i < animals.length; i++) {
                (function(i) {
                    this.print = function() {
                        console.log('#' + i + ' ' + this.species + ': ' + this.name);
                    }
                    this.print();
                }).call(animals[i], i);
            }
            //#0 Lion: King
            //#1 Whale: Fail

            function greet() {
                var reply = [this.person, 'Is An Awesome', this.role].join(' ');
                console.log(reply);
            }
            var i = {
                person: 'Douglas Crockford',
                role: 'Javascript Developer'
            };
            greet.call(i); // Douglas Crockford Is An Awesome Javascript Developer

 

Function.prototype.apply()

fun.apply(thisArg[, argsArray])

 apply() 方法在指定 this 值和参数(参数以数组或类数组对象的形式存在)的情况下调用某个函数。

            //使用apply来链接构造器,本质还是继承
            Function.prototype.construct = function(aArgs) {
                var fConstructor = this,
                    fNewConstr = function() {
                        fConstructor.apply(this, aArgs);
                    };
                fNewConstr.prototype = fConstructor.prototype;
                return new fNewConstr();
            };
            //ES5的另一种写法
//            Function.prototype.construct = function(aArgs) {
//                var oNew = Object.create(this.prototype);
//                this.apply(oNew, aArgs);
//                return oNew;
//            };

            function MyConstructor() {
                for (var nProp = 0; nProp < arguments.length; nProp++) {
                    this["property" + nProp] = arguments[nProp];
                }
            }
            var myArray = [4, "Hello world!", false];
            var myInstance = MyConstructor.construct(myArray);
            console.log(myInstance.property1); // logs "Hello world!"
            console.log(myInstance instanceof MyConstructor); // logs "true"
            console.log(myInstance.constructor); // logs "MyConstructor"

#?

            function Event() {
                this.handlers = [];
            }
            Event.prototype.subscribe = function(handler) {
                this.handlers.push(handler);
            };
            Event.prototype.unsubscribe = function(handler) {
                var index = this.handlers.indexOf(handler);
                if (-1 !== index) {
                    this.handlers.splice(index, 1);
                }
            };
            Event.prototype.emit = function() {
                var args = arguments;
                this.handlers.forEach(function(handler) {
                    handler.apply(null, args); //null改成 this、that好像没差
                });
            };

            var event = new Event();
            function f() {
                f.calls = (f.calls || 0) + 1;
                f.args = Array.prototype.slice.call(arguments);
            }
            event.subscribe(f);
            event.emit(1, 'foo', true);
            console.log(f.calls);
            console.log(f.args);
            //event.unsubscribe(f);//不再受影响
            event.emit(2);
            console.log(f.calls);
            console.log(f.args);
            event.emit(2233);
            console.log(f.calls);
            console.log(f.args);

 

 

 

Function.prototype.bind()

#ECMAscript5

开销较高,保留使用
fun.bind(thisArg[, arg1[, arg2[, ...]]])
//绑定函数,当调用这个绑定函数时,绑定函数会以创建它时传入 bind()方法的第一个参数作为 this,
//传入 bind() 方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。

 

#本质

 

            Function.prototype.bind = function(context) {
                var self = this;
                return function() {
                    self.apply(context);
                }
            };

            function bind(fn, context) {
                return function() {
                    return fn.apply(context, arguments);
                };
            }

            function bind(fn, context) {
                var args = Array.prototype.slice.call(arguments, 2);
                return function() {
                    var innerArgs = Array.prototype.slice.call(arguments),
                        finalArgs = args.concat(innerArgs);
                    return fn.apply(context, finalArgs);
                };
            }

 

#

            //bind() 最简单的用法是创建一个函数,使这个函数不论怎么调用都有同样的 this 值。
            this.x = 9;
            var module = {
                x: 81,
                getX: function() {
                    return this.x;
                }
            };
            console.log(module.getX()); // 81
            var retrieveX = module.getX; //this指向window
            console.log(retrieveX()); // 9, 
            var boundGetX = retrieveX.bind(module); //this指向module
            console.log(boundGetX()); // 81

 

#

            var User = {
                count: 1,
                getCount: function() {
                    return this.count;
                }
            };
            console.log(User.getCount()); // 1
            var func = User.getCount;
            console.log(func()); // indefined
            func = User.getCount.bind(User);
            console.log(func());// 1

 

bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。

    function list() {
                return Array.prototype.slice.call(arguments);
            }
            var list1 = list(1, 2, 3);
            console.log(list1); // [1, 2, 3]
            var leadingThirtysevenList = list.bind(undefined, 37);
            //console.log(leadingThirtysevenList);
            var list2 = leadingThirtysevenList();
            console.log(list2); // [37]
            var list3 = leadingThirtysevenList(1, 2, 3);
            console.log(list3); // [37, 1, 2, 3]

 

在默认情况下,使用 window.setTimeout() 时,this 关键字会指向 window (或全局)对象。
当使用类的方法时,需要 this 引用类的实例,你可能需要显式地把 this 绑定到回调函数以便继续使用实例。

    function LateBloomer() {
                this.petalCount = Math.ceil(Math.random() * 12) + 1;
            }
            LateBloomer.prototype.bloom = function() {
                window.setTimeout(this.declare.bind(this), 1000);
            };
            LateBloomer.prototype.declare = function() {
                console.log('I am a beautiful flower with ' +
                    this.petalCount + ' petals!');
            };
            var flower = new LateBloomer();
            flower.bloom(); // 一秒钟后, 调用'declare'方法

 #polyfill  

            if (!Function.prototype.bind) {
                Function.prototype.bind = function(oThis) {
                    if (typeof this !== "function") {
                        throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
                    }
                    var aArgs = Array.prototype.slice.call(arguments, 1),
                        fToBind = this,
                        fNOP = function() {},
                        fBound = function() {
                            return fToBind.apply(this instanceof fNOP ? this : oThis || this,
                                aArgs.concat(Array.prototype.slice.call(arguments)));
                        };
                    fNOP.prototype = this.prototype;
                    fBound.prototype = new fNOP();
                    return fBound;
                };
            }

 

posted @ 2016-04-04 08:01  晴明桑  阅读(450)  评论(0)    收藏  举报