首页>前端教程>JavaScript教程

函数作为对象的属性和方法(call、apply、bind)

我不会java,但是看过一些文章,说的是java里面的函数必须有一个对象带着才能使用,比如obj.fn(),但是在JavaScript中,函数作为一个独立的个体,不需要obj对象作为大哥带着小弟玩儿了,因为函数在JavaScript中,本身就是对象,所以把函数也叫做一等公民。函数可以单独使用,也可以在对象下面被调用,还可以当作值传递,反正掌握了函数,JavaScript就算会了一半儿。

1、函数作为对象

函数也是对象,也有作为对象的特性,拥有自己的属性和方法。

1.1 属性

1.1.1 length属性

函数的length属性表示形参的个数。函数内部有一个arguments对象,arguments对象的length属性表示实参的个数。

function foo(a,b){
    console.log(a + b);
    console.log(arguments.length); // 这个arguments只能在函数内部访问,在函数外部访问返回null。
}
console.dir(foo);//返回foo对象
console.log(foo.arguments); // null
console.log(foo.length);// 2

1.1.2 name属性

name属性很多浏览器早就支持了,但到ES6中才被纳入标准,该属性返回函数的名字。IE浏览器不支持,返回undefined。

//函数声明
function foo(){
    console.log(foo.name);
}
console.log(foo.name); // foo
foo(); // foo

//函数表达式
let fn1 = function () {
    console.log(fn1.name); 
}
console.log(fn1.name); //fn1
fn1(); // fn1

//有名的函数表达式
let fn2 = function timer(){
    console.log(fn2.name);
}
console.log(fn2.name);//timer
fn2();//timer
timer(); //ReferenceError: timer is not defined

//匿名函数,返回空字符串。通过arguments.callee可以访问函数本身
(function () {
    console.log(arguments.callee.name)
})();

1.2 方法

1.2.1 apply()和call()

apply()和call()这两个方法,它们的作用是一样的,都表示在特定的作用域中调用函数,它们都可以改变函数中this对象的值。

函数声明在某一个作用域中,执行又可能在另一个作用域中,函数内部的this对象是在函数被调用的时候才能确定的,就表示当前调用函数的对象,也表示函数执行时的作用域范围。

通过call()和apply()方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

let fn1;
var name = 'window';
function foo(a,b){
    let c = a + b;
    // 在函数内部声明的函数
    function fn(){
        // 访问了外层函数的变量c
        console.log(c);
        console.log(this.name);
    }
    // 把fn作为值传递给了外部变量fn1,构成了闭包。
    fn1 = fn;
}
foo(1,2); //先执行之后,fn1才有值。
//执行获得的fn函数,闭包产生。
fn1();  // 3 'window'

// 创建一个对象
let obj = {
    name:'obj'
}
// 把函数fn1的值fn函数内部的this指向obj对象。
// 函数在哪里执行和在哪里声明没有关系。可以执行在一个作用域中,声明在另一个作用域中。因为函数是一个对象,可以被引用到任何地方执行。
fn1.call(obj); // 3 'obj'
fn1.apply(obj); // 3 'obj'

//call()这种方法其实等价于
obj.fn2 = fn1;
obj.fn2();
delete obj.fn2;
//为对象扩充属性
function car(color,name){
    this.color = color;
    this.name = name;
    console.log(this.color,this.name);
}
var obj1 = {
    id:1,
};
var obj2 = {
    id:2,
};
var obj3 = {
    id:3
};
// 扩充作用域的好处,就是方法和对象之间没有任何耦合关系。
car.call(obj1,'red','奔驰');
car.call(obj2,'black','宝马');
car.apply(obj3,['white','大众']);
console.log(obj1,obj2,obj3);

//计算商品的总价
let book1 = {
    name:'三国演义',
    count:125,
    price:'25元'
}
let book2 = {
    name:'隋唐英雄传',
    count:1250,
    price:'25元'
}

//统计书籍的价格,让方法和对象之间没有耦合关系。
function getTotalPrice(){
    return (this.count * parseFloat(this.price)).toFixed(2);
}
console.log(getTotalPrice.call(book1));
console.log(getTotalPrice.call(book2));

apply()方法接收两个参数:第一个表示函数运行的作用域(即函数中this指向谁),也可以是arguments对象。;第二参数是一个数组,表示函数的参数。

function toStr(){
    return Array.prototype.join.call(arguments,':');
}
console.log(toStr(1,2,3)); // 1:2:3

call()方法和apply()方法类似,唯一区别就是参数的形式不一样,call()接收的是参数列表,apply()接收的是数组。

//如果没有指定的对象,可以用null表示。
let result = Math.max.apply(null,[10,25,3,45,20]);
console.log(result)
const oLis = document.querySelectorAll('li');
//NodeList集合不支持map方法。
//让数组的map方法的this指向oLis对象,后面的函数是map()需要的参数。
let newArr = Array.prototype.map.call(oLis,function(item,index){
    return item.innerHTML;
})

1.2.2 bind()

ES5 IE9+,这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

适合需要反复调用函数的时候使用。

let obj = {
    vip:0.8
}

//根据vip折扣打折
function getSum(price,count){
    return (price * count) * this.vip;
}
//把getSum的this绑定到obj对象上,返回一个新的函数
let getObjSum = getSum.bind(obj);
//两个函数不相等,不是同一个函数
console.log(getObjSum == getSum) //false
console.log(getObjSum(20,100));

//还可以先传一部分参数,再返回的函数中继续传后面的参数。
function getSum(price,count){
    return (parseFloat(price) * count).toFixed(2) * this.vip;
}
//函数调用时只传递一部分参数进行调用,函数会返回一个新函数去处理剩下的参数,这是函数柯里化的应用
let getObjSum = getSum.bind(obj,'20元');
console.log(getObjSum(1000));

2、举例

验证数据输入合法性

    <div class="box">
        <div class="row">
            <span>输入0-1000的数字</span>
            <input id="num1">
            <span id="numtips1"></span>
        </div>
        <div class="row">
            <span>输入200-10000的数字</span>
            <input id="num2">
            <span id="numtips2"></span>
        </div>
    </div>
    <script>
        var oInput1 = document.querySelector('#num1');
        var oTips1 = document.querySelector('#numtips1');
        var oInput2 = document.querySelector('#num2');
        var oTips2 = document.querySelector('#numtips2');

        oInput1.oninput = function () {
            var result = checkNum.call(this,0,1000);
            oTips1.innerHTML = result;
        }
        oInput2.oninput = function () {
            var result = checkNum.call(this,200,10000);
            oTips2.innerHTML = result;
        }

        function checkNum(min, max) {
            return this.value >= min && this.value <= max ? '数据合法' : '数据不合法';
        }
    </script>
//遍历学生的信息
let students = [
    {
        name: 'mike',
        birth: '2010-10-15',
        city: '成都'
    },
    {
        name: 'daisy',
        birth: '2009-10-15',
        city: '重庆'
    },
    {
        name: 'Jone',
        birth: '2012-10-15',
        city: '北京'
    }
]
// 遍历学生的信息
students.forEach(function(item){
    // 把函数的this对象绑定在item对象上。
    printStudentInfo.call(item);
})
// 显示学生信息的函数
function printStudentInfo(){
    let age = getAge(this.birth);
    console.log(`姓名是:${this.name},年龄是${age}岁,籍贯是:${this.city}`);
}
// 获得年龄的函数
function getAge(birth){
    return new Date().getFullYear() - parseInt(birth);
}


点赞


1
保存到:

相关文章

发表评论:

◎请发表你卖萌撒娇或一针见血的评论,严禁小广告。

Top