前端总结·基础篇·JS(三)arguments、callee、call、apply、bind及函数封装和构造函数

前端总结系列

目录

1、函数使用
    1.1 函数声明和函数表达式
    1.2 函数封装(自调用函数、闭包)
    1.3 函数属性(arguments、callee)
    1.4 构造函数

2、函数技巧
    2.1 改变函数做用域(call/apply/bind)
    2.2 for循环中的setTimeout问题修复

1、函数使用

字符串和数组都是用来存储数据。须要作一些特定的事情的时候,咱们就会会用函数封装起来。javascript

1.1 函数声明和函数表达式

声明一个函数能够用函数声明或者函数表达式html

  • 函数表达式必须先声明后调用
  • 函数内的变量须要使用var声明,不然会污染全局变量
function hello( name ) { some code ... }  // 函数声明
var hello = function ( name ) { some code ... }  // 函数表达式

// 变量提高 | 函数内部的变量未使用var声明,执行后会致使变量成为全局变量

var age = 22

function showAge(){
    age = 23
    console.log(age)
}

showAge()  // 23 | 执行函数
console.log(age)  // 23 | 测试全局变量age的值

1.2 函数封装(自调用函数、闭包)

实际项目中会引入不少外部JS文件,为了不彼此命名冲突。一般各自都会对内部函数进行封装。(如今更好的方案是使用模块化,模块化之后再总结)前端

自调用函数java

使用自调用函数,把整个JS文件的代码都封装在内。git

  • 这样作的好处是不会污染全局变量,能够很好的减小命名冲突。
  • 最后把对外访问的接口挂载在window上。
(function(){
    // some code ...
    window.myApp = myApp()  // 把对外访问的接口挂载在window上
})()

闭包封装es6

若是你内部使用的是函数表达式,而且不用var声明,变量依旧会泄露到全局。github

// 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')  // 可以访问 |  berg
myApp.showCity('NanChang')  // 可以访问 |  NanChang
showName('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

// 声明构造函数 Human

function Human() {
    this.play = function(){
        console.log('I\'m playing.')
    }
}

// 声明构造函数 Male

function Male() {
    this.sex = 'male'
}

// 声明构造函数 Female

function Female() {
    this.sex = 'female'
}

// 让Male和Female继承Human

Male.prototype = new Human()
Female.prototype = new Human()

// 建立xiaoming对象而且测试继承结果

var xiaoming = new Male()
console.log(xiaoming.sex)  // male
console.log(xiaoming.play())  // I'm playing.

// 建立xiaohong对象而且测试继承结果

var xiaohong = new Female()
console.log(xiaohong.sex)  // female
console.log(xiaohong.play())  // I'm playing.

2、函数技巧

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写出的总结。外加ChokCoco和JavaScript 秘密花园中的细致分析,以及本身平时的一些总结写成此文。

文中的错误还望可以指出,会及时作出修改(哪怕是错别字)。Vue的基本理念如今差很少搞清楚了,大概在下周,会对Vue作一个总结(其实官方文档是最好的总结 ^_^)。

相关文章
相关标签/搜索