js基础(八) 函数
Robin 7/30/2017 javaScript面试
# 知识点一 函数
- 一个函数存在了多面性
- 普通函数:它本是就是一个普通的函数,执行的时候形成私有的作用域(闭包),形参赋值,预解释,代码执行,执行完成后栈内存销毁/不销毁
- 类:它自己的实例,也有一个叫做
prototype
属性是自己的原型,它的实例都是指向自己的原型 - 普通对象:和
var obj = {}
中的obj
一样,就是一个普通的对象,它作为对象可以有一些自己的私有属性,也可以通过__proto__
找到Function.prototype
上的公用属性 - 这三者之间没有任何关系
function Fn() {
var num = 500
this.x = 100
}
Fn.prototype.getX = function() {
console.log(this.x)
}
Fn.aaa = 1000
var f = new Fn // Fn中的this是f
// 实例-->类
f.num // undefined
f.aaa // undefined
// 普通函数
var res = Fn() // Fn中的this是window
console.log(res) // undefined
// 普通对象
Fn.aaa // 1000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 知识点二 函数属性
length
:形参的个数name
:函数名prototype
:类的原型,在原型上定义的方法都是当前这个类实例的公有方法__proto__
:把函数当作一个普通的对象,指向Function
这个类的原型
# 知识点三 函数与对象关系图
下图中的函数与对象:
- 函数:
Fn
、Function
、Object
- 对象:
f
、Fn
、Fn.prototype
、Function
、Function.prototype
、Object
、Object.prototype
万物皆对象
# 知识点四 call
- 作用:首先让原型上的
call
方法执行,在执行call
方法的时候,让方法中的this
指向为第一个参数,然后再把函数执行
var obj = { name: 'obj' }
function fn() {
console.log(this, arguments)
}
fn(1, 2, 3) // --> window
fn.call(obj, 1, 2, 3) // --> obj
1
2
3
4
5
6
2
3
4
5
6
- 手写内置的
call
方法
Function.prototype.myCall = function() {
// 1.让函数中的this指向第一个参数
var args = Array.from(arguments)
var context = args.length > 0 ? args.shift() : window
context.fn = this
// 2.执行函数
context.fn(...args)
delete context.fn
}
function fn() {
console.log(this, arguments)
}
fn(1, 2, 3) // --> window
fn.myCall({name: 'fn'}, 1, 2, 3) // --> {name: 'fn'}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
call(thisArg, arg1, arg2, ...)
方法第一个参数thisArg
是函数运行时this
的指向,arg1, arg2, ...
是传递给函数的参数列表,参数列表 必须列举出来案例:
fn1.call
首先fn1
通过原型链找到Function.prototype
上的call
方法,然后在让call
方法通过原型链找到Function.prototype
的call
(因为call
本身的值也是一个函数,所以同样可以找到Function.prototype
),在带二次再找到call
的时候方法执行,方法中的this
是fn1.call
,首先让这个方法中的this
变为fn2
,然后再让fn1.call
执行,打印出window, 2
function fn1() {
console.log(this, 1)
}
function fn2() {
console.log(this, 2)
}
fn1.call.call(fn2) // --> window, 2
// call实现的为代码
// Function.prototype.call = function() {
// // .....
// this()
// }
// 分析:
// 1. fn1.call --> Function.prototype.call
// 2. fn1.call.call(fn2) --> Function.prototype.call.call(fn2),
// 先让第二个call执行,call中的this是 Function.prototype.call,
// 然后让 Function.prototype.call 中的this变为 fn2,再让 Function.prototype.call 执行
// 3. 最后fn2()执行,打印出 window, 2
Function.prototype.call(fn1)
// 分析:
// 1. Function.prototype 是一个匿名函数或空函数
// 2. 先让call执行,call中的 this 指向 fn1 再让 Function.prototype 执行即匿名函数或空函数执行
// 2. 最后返回 undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- 严格模式
"use strict"
function fn(num) {
console.log(this, num)
}
fn.call() // 在严格模式下this --> undefined
fn.call(null) // 在严格模式下this --> null
fn.call(undefined) // 在严格模式下this --> undefined
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 知识点五 apply
- 核心:和
call
方法的作用一样,在执行apply
方法的时候,让方法中的this
指向为第一个参数,然后再把函数执行 - 语法:
func.apply(thisArg, [argsArray])
- 与
call
的异同点:
- 和
call
一样的是,方法第一个参数thisArg
是函数运行时this
的指向 - 和
call
不一样的是argsArray
是传递给函数的参数列表,参数列表 必须是数组
- 和
function fn(a, b) {
console.log(this, a, b)
}
var obj = {
name: 'apply'
}
fn.apply(obj, [1, 2])
1
2
3
4
5
6
7
2
3
4
5
6
7
# 知识点六 bind
- 核心:
bind()
方法创建一个新的函数,在bind()
被调用时,这个新函数的this
被指定为bind()
的第一个参数,而其余参数将作为新函数的参数,供调用时使用,参数列表 必须列举出来 - 语法:
function.bind(thisArg[, arg1[, arg2[, ...]]])
function fn(a, b) {
console.log(this, a, b)
}
var obj = {
name: 'apply'
}
var f = fn.bind(obj, 1, 2)
f()
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 知识点七 括号表达式
- 括号表达式,里面有多项,只执行最后一项
- 括号里面有多项,只执行最后一项时,最后一项函数里面的
this
是window
function fn1() {
console.log(this, 'fn1')
}
function fn2() {
console.log(this, 'fn2')
}
var obj = {
name: 'obj',
fn: fn2
}
(fn2, obj.fn)() // 最后一项执行,this是window,因为相当于拷贝了一份 obj.fn 函数在这里
(obj.fn)() // this 是 obj
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 知识点八 回调函数
# 传参方式
- 将回调函数的参数作为与回调函数同等级的参数进行传递
- 回调函数的参数在调用回调函数内部创建
# 注意
- 匿名回调函数的中
this
是window
# 案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// Polyfill
if(!!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function')
}
const context = thisArg || window
for (let i = 0; i < this.length; i++) {
callback && callback.call(context, this[i], i, this)
}
}
}
if(!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function')
}
const context = thisArg || window
const res = []
for (let i = 0; i < this.length; i++) {
res[i] = callback && callback.call(context, this[i], i, this)
}
return res
}
}
const obj = { name: 'test' }
const aryCustom = [12, 23, 34, 45, 56]
aryCustom.forEach(function(item, index, input) {
console.log(item, index, input, this)
}, obj)
const resCustom = aryCustom.map(function(item, index, input) {
console.log(item, index, input, this)
return item * 10
}, obj)
console.log(aryCustom)
console.log(resCustom)
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 知识点九 柯里化函数
柯里化函数思想:一个js预处理的思想
核心思想:利用函数执行可以形成一个不销毁的私有作用域的原理,把需要预处理的内容都存在这个不销毁的作用域中,并且返回一个小函数,以后我们执行的都是小函数,在小函数中把之前预存储的值进行相关的操作处理即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
height: 100vh;
width: 100vw;
}
</style>
</head>
<body>
<script>
function bind() {
const args = arguments
let callback = null
let thisArg = window
let rest = []
if (args.length === 0) {
throw new Error('bind need a function argument')
} else if (args.length === 1) {
callback = arguments[0]
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function')
}
} else if (args.length >= 2) {
callback = arguments[0]
thisArg = arguments[1]
if (Array.prototype.toString.call(thisArg) !== '[object Object]') {
throw new TypeError(thisArg + ' is not a object')
}
rest = Array.prototype.slice.call(arguments, 2)
}
thisArg.fn = callback
return function() {
thisArg.fn(...rest, ...arguments)
delete thisArg.fn
}
}
const obj = { name: 'test' }
function fn(num1, num2) {
console.log(arguments, this)
}
// window.setTimeout(fn.bind(obj), 0)
// window.setTimeout(bind(fn, obj, 100), 0)
// document.body.onclick = function() {
// console.log(arguments)
// }
document.body.onclick = bind(fn, obj, 100, 200)
</script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 知识点十 函数编程
# 知识点十一 案例
# 案例一 求数组最大最小值
// 方式一
var ary = [22, 34, 56, 43, 12, 99, 78, 23, 19]
ary.sort((a, b) => a -b)
var max = ary[ary.length - 1]
var min = ary[0]
console.log(max, min)
// 方式二
var ary2 = [22, 34, 56, 43, 12, 99, 78, 23, 19]
var max2 = eval(`Math.max(${ary2.join()})`)
var min2 = eval(`Math.min(${ary2.join()})`)
console.log(max2, min2)
// 方式三
var ary3 = [22, 34, 56, 43, 12, 99, 78, 23, 19]
var max3 = Math.max.apply(null, ary3)
var min3 = Math.min.apply(null, ary3)
console.log(max3, min3)
// 方式四
var ary4 = [22, 34, 56, 43, 12, 99, 78, 23, 19]
var max4 = Math.max.call(...ary4)
var min4 = Math.min.call(...ary4)
console.log(max4, min4)
// 方式五
var ary5 = [22, 34, 56, 43, 12, 99, 78, 23, 19]
var max5 = Math.max(...ary5)
var min5 = Math.min(...ary5)
console.log(max5, min5)
// 方式六
var ary6 = [22, 34, 56, 43, 12, 99, 78, 23, 19]
var max6 = ary6[0]
var min6 = ary6[0]
for(var i = 1; i < ary6.length; i++) {
var cur = ary6[i]
max6 = cur > max6 ? cur : max6
min6 = cur < min6 ? cur : min6
}
console.log(max6, min6)
// 方式七
var ary7 = [22, 34, 56, 43, 12, 99, 78, 23, 19]
function getMax(prev, next) {
return Math.max(prev, next)
}
function getMin(prev, next) {
return Math.min(prev, next)
}
var mxa7 = ary7.reduce(getMax)
var min7 = ary7.reduce(getMin)
console.log(mxa7, min7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 案例二 求平均数
// 方式一
function avgFn() {
var args = []
for(i = 0;i < arguments.length;i++) {
args.push(arguments[i])
}
args.sort((a, b) => a-b)
args.pop()
args.shift()
var res = eval(args.join('+')) / args.length
return res.toFixed(2)
}
var avg = avgFn(9.8, 9.7, 10, 9.9, 9.0, 9.8, 3.0)
console.log(avg)
// 方式二
function avgFn() {
// var args = [].slice.apply(arguments)
var args = Array.prototype.slice.apply(arguments)
args.sort((a, b) => a-b)
args.pop()
args.shift()
var res = eval(args.join('+')) / args.length
return res.toFixed(2)
}
var avg = avgFn(9.8, 9.7, 10, 9.9, 9.0, 9.8, 3.0)
console.log(avg)
// 方式三
function avgFn() {
// var args = [].slice.call(arguments)
var args = Array.prototype.slice.call(arguments)
args.sort((a, b) => a-b)
args.pop()
args.shift()
var res = eval(args.join('+')) / args.length
return res.toFixed(2)
}
var avg = avgFn(9.8, 9.7, 10, 9.9, 9.0, 9.8, 3.0)
console.log(avg)
// 方式四
function avgFn() {
[].sort.call(arguments, (a, b) => a-b);
[].pop.call(arguments);
[].shift.call(arguments);
var res = eval([].join.call(arguments, '+')) / arguments.length
return res.toFixed(2)
}
var avg = avgFn(9.8, 9.7, 10, 9.9, 9.0, 9.8, 3.0)
console.log(avg)
// 方式五
function avgFn() {
var args = Array.from(arguments)
args.sort((a, b) => a-b)
args.pop()
args.shift()
var res = args.reduce((pre, next) => pre + next) / args.length
return res.toFixed(2)
}
var avg = avgFn(9.8, 9.7, 10, 9.9, 9.0, 9.8, 3.0)
console.log(avg)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# 案例三 类数组转数组
var utils = {
listToArray: function(likeAry) {
var ary = []
try {
ary = Array.prototype.slice.call(likeAry)
} catch(e) {
for(var i = 0;i < likeAry.length;i++) {
ary[ary.lenght] = likeAry[i]
}
}
return ary
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 参考
理解与使用Javascript中的回调函数 (opens new window)