JavaScript closure: difference between call and apply as well as use them to create inheritance

Resume

Today, in an interview, speaking of closure, the interviewer asked me about the difference between call() and apply(). And I didn’t use them before. After searching for a while, I found that we can implement completely a class with JavaScript. We can implement a constructor by using call() and apply(). I thought that we can only implement a class without any inheritance before. This passage will describe what I have found.


call()

Syntax:
function.call(thisArg, arg1, arg2, ...)


Suppose we have a function like this,

var lang = {'languages':['Python', 'Haskell', 'C++', 'Java']};
var favoriteLanguages = function(){
    for(var i = 0; i < this.languages.length; i++){
        console.log('My favorite languages is', this.languages[i]);
    }
}
favoriteLanguages.call(lang);

and it will print

My favorite languages is Python
My favorite languages is Haskell
My favorite languages is C++
My favorite languages is Java

on console.


So, consequently we can say that function.call() will apply arguments to function as local variable.


apply()

Syntax:
fun.apply(thisArg, [argsArray])



Actually it’s quite simular to call(). The only difference is we pass arguments call() while we pass an argument and a list of argument to apply().
Moreover, usually we pass this as the first argument to apply.
It’s all the same when we do:

var lang = {'languages':['Python', 'Haskell', 'C++', 'Java']};
var favoriteLanguages = function(){
    for(var i = 0; i < this.languages.length; i++){
        console.log('My favorite languages is', this.languages[i]);
    }
}
favoriteLanguages.applu(lang);//We changed here.



But when we try to do something different, there will be some differences.

var languages= {'prog_languages':['Python', 'Haskell', 'C++', 'Java'],'languages':['English', 'Français', '中文']};

var prog_l = function(){
    for(var i = 0; i < this.prog_languages.length; i++){
        console.log('I like', this.prog_languages[i], 'as a programming language.');
    }
}

var lang = function(){
    for(var i = 0; i < this.languages.length; i++){
        console.log('I like', this.languages[i], 'as a language.');
    }
}

var dispatch = function(method0, args0, method1, args1){
    method0.apply(args0);
    method1.apply(args1);
};

dispatch(lang,languages,prog_l,languages);

When we run codes above, it will show

I like English as a language.
I like Français as a language.
I like 中文 as a language.
I like Python as a programming language.
I like Haskell as a programming language.
I like C++ as a programming language.
I like Java as a programming language

in console.


It means that when we are not certain how many parameters we will pass, it’s better to use apply() than call(). Because apply() put the first argument into this and take the send argument which is a list as arguments applying to function.


Inheritance

Actually it’s a little bit like the object principle in most programming languages such as Java and C++.
Let’s see an example on mozilla.org.

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

function Food(name, price) {
    Product.call(this, name, price);
    this.category = 'food';
}

function Toy(name, price) {
    Product.call(this, name, price);
    this.category = 'toy';
}

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

console.log(cheese.price);

When we run these codes, it will return 5 which is exactly the price of cheese.
But why?
When we instantiate Food, then we jump into the function Food(name, price), then we invoke the function Product.call(this, name, price) and pass this to Product(name, price). In Product(name, price), actually we are modifying Food, because we passed this in Product.call(this, name, price). So that this points to object Food. And we assign name and price to this(which is the reference of Food).
So, it’s the object Food which is being modified and finally we have name, price and category in Food.


Pretty like in Java, isn’t it?
In Java, we may write

class Product{
    protected String name;
    protected int price;
    public Product(String name, int price){
        this.name = name;
        this.price = price;
        if (price < 0) {
            throw new RangeError('Cannot create product ' + this.name + ' with a negative price'); //RangeError is not defined, because it's not important in this case.
        }
    }
}

and

class Food extends Product{
    protected String category = "food";
    public Food(String name, int price){
        super(name, price);
    }
}

References

阅读更多
版权声明:本文章禁止商业使用,非商用转载必须注明出处和作者。作者保留一切权利。 https://blog.csdn.net/voyager3/article/details/52355908
文章标签: javascript inheritance
上一篇How to call a closure that is a class variable?
下一篇Difference between prototype and this in JavaScript
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭