我不会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); }
发表评论:
◎请发表你卖萌撒娇或一针见血的评论,严禁小广告。