前端总结·基础篇·JS(三)arguments、callee、call、apply、bind及函数封装和构造函数
来源:互联网 发布:淘宝folk老王真货么 编辑:程序博客网 时间:2024/05/16 01:43
前端总结系列
- 前端总结·基础篇·CSS(一)布局
- 前端总结·基础篇·CSS(二)视觉
- 前端总结·基础篇·CSS(三)补充
- 前端总结·基础篇·JS(一)原型、原型链、构造函数和字符串(String)
- 前端总结·基础篇·JS(二)数组深拷贝、去重以及字符串反序和数组(Array)
- 前端总结·基础篇·JS(三)arguments、callee、call、apply、bind及函数封装和构造函数
目录
一、函数使用 1.1 函数声明和函数表达式 1.2 函数封装(自调用函数、闭包) 1.3 函数属性(arguments、callee) 1.4 构造函数二、函数技巧 2.1 改变函数作用域(call/apply/bind) 2.2 for循环中的setTimeout问题修复
一、函数使用
字符串和数组都是用来存储数据。需要做一些特定的事情的时候,我们就会会用函数封装起来。
1.1 函数声明和函数表达式
声明一个函数可以用函数声明
或者函数表达式
。
- 函数表达式必须先声明后调用
- 函数内的变量需要使用var声明,否则会污染全局变量
function hello( name ) { some code ... } // 函数声明var hello = function ( name ) { some code ... } // 函数表达式// 变量提升 | 函数内部的变量未使用var声明,执行后会导致变量成为全局变量var age = 22function showAge(){ age = 23 console.log(age)}showAge() // 23 | 执行函数console.log(age) // 23 | 测试全局变量age的值
1.2 函数封装(自调用函数、闭包)
实际项目中会引入很多外部JS文件,为了避免彼此命名冲突。通常各自都会对内部函数进行封装。(现在更好的方案是使用模块化,模块化以后再总结)
自调用函数
使用自调用函数,把整个JS文件的代码都封装在内。
- 这样做的好处是不会污染全局变量,可以很好的减少命名冲突。
- 最后把对外访问的接口挂载在window上。
(function(){ // some code ... window.myApp = myApp() // 把对外访问的接口挂载在window上})()
闭包封装
如果你内部使用的是函数表达式,并且不用var声明,变量依旧会泄露到全局。
// a.js(function(){ function myApp() { // 定义showName方法 function showName(name) { console.log(name) } // 定义showAge方法 function showAge(age){ console.log(age) } // 返回一个对象 return { showName: showName, showAge: showAge } } window.myApp = myApp()})()
功能扩展
引入上面的a.js文件,并且创建b.js来写程序主逻辑。
- 你也可以写在一个文件,写在两个文件是为了把接口和控制器分开,方便管理。
- 你可以修改上面提供的方法,或者新添加一条方法。
//b.js// 修改已有方法myApp.showName = function (name) { console.log('Call me '+name)}// 定义新方法myApp.showCity = function (city) { console.log(city)}myApp.showName('berg') // 能够访问 | bergmyApp.showCity('NanChang') // 能够访问 | NanChangshowName('berg') // 不能访问 | Uncaught ReferenceError: showName is not defined
1.3 函数属性(arguments、callee)
arguments是用来存放实参的,可以通过下标访问实参的值。callee指向当前执行的函数,可以在递归的时候用。具体递归场景以及代码,请见MDN。
- callee不能实现尾递归
- callee在ES5严格模式中禁止使用
- caller指向调用当前函数的函数(已废弃)
function show(name,age) { console.log(name) // berg console.log(age) // 22 console.log(arguments) // ["berg", 22] console.log(arguments.callee) // function show(name,age) {}}show('berg',22)
1.4 构造函数
ES6可以使用Class实现继承,以后在单独总结ES6的时候会提到。推荐一个非常好的ES6系列教程,深入浅出ES6
// 声明构造函数 Humanfunction Human() { this.play = function(){ console.log('I\'m playing.') }}// 声明构造函数 Malefunction Male() { this.sex = 'male'}// 声明构造函数 Femalefunction Female() { this.sex = 'female'}// 让Male和Female继承HumanMale.prototype = new Human()Female.prototype = new Human()// 创建xiaoming对象并且测试继承结果var xiaoming = new Male()console.log(xiaoming.sex) // maleconsole.log(xiaoming.play()) // I'm playing.// 创建xiaohong对象并且测试继承结果var xiaohong = new Female()console.log(xiaohong.sex) // femaleconsole.log(xiaohong.play()) // I'm playing.
二、函数技巧
2.1 改变函数作用域(call/apply/bind)
当需要改变上下文的this的时候,可以使用call/apply/bind。(更详细的解释请见ChokCoco,需要用call实现继承请见MDN)
- 三种方法的第一个参数都是this的上下文
- apply第二个参数是数组,call和bind后面都是接单个参数
- call和apply默认会自动执行,bind需要在后面加()来自动执行
- bind是ES5语法,支持IE9+
/* 以下以数组合并为例子 * 使用不同的方法之前,请确保a和b为默认值 */var a = [1,2,3] // 测试用的默认值var b = [4,5,6] // 测试用的默认值a.push(b) // 直接使用push(不符合预期)console.log(a) // [1, 2, 3, Array[3]]// call方法(在这种情况下要逐个输入参数,不太方便)Array.prototype.push.call(a,4,5,6)console.log(a) // [1, 2, 3, 4, 5, 6]// apply方法(第二个参数直接传入数组,非常适用这种场景)Array.prototype.push.apply(a,b)console.log(a) // [1, 2, 3, 4, 5, 6]// bind方法Array.prototype.push.bind(a,4,5,6)() // 注意看,这里需要加一个自动执行函数console.log(a) // [1, 2, 3, 4, 5, 6]
2.2 for循环中的setTimeout问题修复
setTimeout有自己的this。如果在外层放一个for循环,意味着会一次性执行完,而没有起到延时的作用。解决方案是使用闭包。此处主要参考JavaScript 秘密花园
- setTimeout是一个定时执行函数。接受两个参数,第一个是执行的函数,第二个是延迟执行的时间。通常用在登陆之后,提示几秒钟之后跳转到首页(现在基本不这样做了)。
- setInterval和setTimeout基本一致。只是第二个时间参数,表示的是每多长事件执行一次。
- 第二个参数的时间单位是毫秒(1000表示为1秒)
// 使用闭包前for(var x = 0; x<10; x++) { setTimeout(run,1000) // 10}function run() { console.log(x)}// 使用闭包后for(var x = 0; x<10; x++) { setTimeout((function (x) { return function() { console.log(x) // 0 1 2 3 4 5 6 7 8 9 } })(x),1000)}
总结
全文主要参考以下网站
- MDN
- JavaScript 秘密花园
- ChokCoco
推荐的教程
- JavaScript系列 @ 廖雪峰
- JavaScript 标准参考教程(alpha) @ 阮一峰
- 深入理解JavaScript @ 汤姆大叔
- 深入浅出ES6
- ECMAScript 6 入门 @ 阮一峰
- 张鑫旭博客
全文主要是参考MDN写出的总结。外加ChokCoco和JavaScript 秘密花园中的细致分析,以及自己平时的一些总结写成此文。
文中的错误还望能够指出,会及时做出修改(哪怕是错别字)。Vue的基本理念现在差不多搞清楚了,大概在下周,会对Vue做一个总结(其实官方文档是最好的总结 ^_^)。
0 0
- 前端总结·基础篇·JS(三)arguments、callee、call、apply、bind及函数封装和构造函数
- arguments、callee、call、apply、bind及函数封装和构造函数
- 函数的arguments,caller,callee,call,apply
- javascript 函数 function arguments caller callee apply() call()
- [前端] call、apply、bind 和 aguments、caller、callee解析
- JS 中的call,apply,bind 和 caller, callee
- js中call,apply与bind三函数
- JS 函数中的arguments,call,apply
- js中的apply/call/caller/callee/bind
- js中call、apply、bind函数
- arguments,caller,callee,apply,call
- caller,arguments.callee,call,apply
- JS 函数call和apply
- 【JS--基础--函数】--函数的通用方法-call()和apply()
- javascript的函数-重点介绍callee,call和apply
- javascript 的函数调用过程中的arguments,callee,caller,apply,call
- 理解JavaScript中的arguments,callee,caller,apply 和call
- 【JS--基础--函数】--隐含参数对象arguments的callee 属性
- leetcode-Length of the longest substring without repeating characters
- 【Leetcode】373. Find K Pairs with Smallest Sums
- leetcode-Power of Two
- 用Visual Studio Code Debug世界上最好的语言
- JavaScript数据结构——队列的实现
- 前端总结·基础篇·JS(三)arguments、callee、call、apply、bind及函数封装和构造函数
- erlang:send_after和erlang:start_timer的使用解释
- 初用Linux, 安装Ubuntu16.04+NVIDIA387+CUDA8.0+cudnn5.1+TensorFlow1.0.1
- DapperPoco -- 基于Dapper的、轻量级的、高性能的、简单的、灵活的ORM框架
- wxpython分割窗研究(解决sashPosition=0无效的BUG)
- WPF: 在 MVVM 设计中实现对 ListViewItem 双击事件的响应
- 一段Java有意思的代码
- Spring Boot启动过程(六):内嵌Tomcat中StandardHost、StandardContext和StandardWrapper的启动
- Leetcode 500. Keyboard Row