A26-JS函数

2018-01-24  本文已影响0人  半斋

Function -MDN

var f = function(a,b){
  return a+b
}

语法:new Function ([arg1[, arg2[, ...argN]],] functionBody)
参数:
arg1, arg2, ... argN
被函数使用的参数的名称必须是合法命名的。参数名称是一个有效的JavaScript标识符的字符串,或者一个用逗号分隔的有效字符串的列表;例如“×”,“theValue”,或“A,B”。
functionBody
一个含有包括函数定义的JavaScript语句的字符串

函数总是会返回一个值

function fn(){
  console.log('函数')
  return undefined // 如果你不写就会自动添加这样的一句
}

函数的5种声明方式

function 有一个属性:name

  1. 具名函数
function f(x,y){
  console.log(x+y) // 打印什么和返回什么没有必然联系
  return undefined // 必须有一个return,没有的话也会被自动添加本句
}
f.name // 'f'
  1. 匿名函数
    它不能单独使用,可以赋给一个变量来使用函数表达式
 var f
 f = function(x,y){
     return x+y
 }
 f.name // 'f'
  1. 具名函数赋值 -阮一峰
function f(){}
f.name // 'f'
console.log(f) // 'function f(){}'

var f2 = function fn(){}
f2.name // 'fn'
console.log(fn) // Uncaught ReferenceError: fn is not defined
// 只能在 function fn(){}这个函数里面访问到fn,在外面无效
// js 的不一致性
  1. window.Function 函数对象
var f = new FUnction('x', 'y', 'return x+y')
f.name // 'anonymous'

var n=1
var fn = new Function('x', 'y', 'return x+' + n + '+y' ) 
console.log(fn) // f anonymous(x,y){ return x+1+y }
fn(1,2) // 4
  1. 箭头函数 -MDN
var f = (x,y) => {return x+y}
f.name // 'f'

var f2 = (x,y) => x+y // 只有一句 return 的时候,可以将 return 和 {} 省略
var f3 = n => n*n // 如果参数只有一个,() 可以省略

函数的本质

函数是一段可以反复调用的代码块。
函数还能接受输入的参数,不同的参数会返回不同的值。
函数和数据不同,不能直接使用,只能调用

// 定义一个 n
var n=2
// 然后直接使用它就好了
var m = n // 比如将它赋值给 m
console.log(n) // 或者打印出来

// 定义一个函数
function f(x,y){
  return x+y
}
f  // 打印出 f 函数,等于啥也没做,return x+y 并没有执行
// 函数只能调用(call)
f(1,2) // 3

函数是怎样储存的


函数也是对象的一种,在stack中储存地址,在heap中储存对象,保存的是字符串(就是你定义的函数,参数、函数体等),通过Function.prototype里的call()来调用函数体
我们来试着用对象来做一个这样的函数

var f = {}
f.name = 'f' // 函数本身就有的属性
f.params = ['x','y']  // 模拟参数
f.functionBody = 'console.log(1)'  // 模拟函数体,不管参数还是函数体,都是字符串形式
f.call = function(){
  return window.eval(f.functionBody)
}
f.call() 
// 1
// undefined, console.log()的返回值
// 这就是为什么说函数是对象,函数调用实际上就是eval()函数的函数体的过程
// f与f.call()区别:f是变量,是函数(这里用的是对象),f.call()是调用函数

所以函数就是可以执行代码的对象
eval() -MDN
eval()函数会将传入的字符串当做JavaScript代码进行执行。

f(1,2) 与 f.call(undefined, 1, 2)


前者是语法糖,后者才是真正的写法
理解这两者的区别才能更好的理解this
SegmentFault

call()怎么用 -MDN

function f(x,y){ return x+y }
f.call(undefined, 1, 2) // 3

call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
语法:fun.call(thisArg, arg1, arg2, ...)
参数:
thisArg
在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,
如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),
同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
在严格模式下,this将保持他进入执行上下文时的值,所以下面的this将会默认为undefined。(详见this-mdn)
arg1, arg2, ...
指定的参数列表。
返回值:返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined。
描述:
可以让call()中的对象调用当前对象所拥有的function。
你可以使用call()来实现继承: 写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。

this -MDN-阮一峰 与 arguments MDN

什么是this,在以下代码中
f.call(undefined, 1, 2)
undefined就是this
12就是arguments
arguments是伪数组别忘了
或者说:
call的第一个参数可以用this得到
call的后面所有参数可以用arguments得到

// Eg
var f = function(){
  console.log(this)
  console.log(this === window)
  console.log(arguments)
}
f.call(undefined, 1,2,3)
// log this -> window 对象 原因见上文-call()怎么用
// true
// log arguments -> [1,2,3] 还有一些属性api见mdn

var f2 = function(){
  'use strict'  // use 严格模式
  console.log(this)
  console.log(this === window)
  console.log(arguments)
}
f2.call(undefined)
// undefined
// false
// []

f2.call(7)
// 7 false []
 f2.call('foo')
 // 'foo' false []

var f3 =  function() {
  console.log(Object.prototype.toString.call(this));
}
//原始值 1 被隐式转换为对象
f3.call(1); // [object Number]

注意: 使用 call 和 apply 函数的时候,如果传递的 this 值不是一个对象,JavaScript 将尝试使用内部 ToObject 操作将其转换为对象。因此,如果传递的值是一个原始值比如 7 或 'foo',那么就会使用相关构造函数将它转换为对象,所以原始值 7 通过new Number(7)被转换为对象,而字符串'foo'使用 new String('foo') 转化为对象。(这一点上文也有描述)
this 也是为了长得像 java 而诞生的,和 new 一样

call stack 调用栈

表示函数或子例程像堆积木一样存放,以实现层层调用。
别忘了栈是先入后出。


嵌套调用
function sum(n){
    if(n==1){
        return 1
    }else{
        return n + sum.call(undefined, n-1)
    }
}

sum.call(undefined,5) //15

sum.call(undefined, 100000)
// Uncaught RangeError: Maximum call stack size exceeded
// 最大调用栈大小超过

作用域 -MDN-阮一峰

面试题

拿到代码直接做——必然会错。请先提升声明

// 1.
var a = 1
function f1(){
    alert(a) // 是多少
    var a = 2
}
f1.call() // undefined

// 2.
var a = 1
function f1(){
    var a = 2
    f2.call()
}
function f2(){
    console.log(a) // 是多少
}
f1.call() // 1

// 3.
<ul>
  <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li>
</ul>
var liTags = document.querySelectorAll('li')
for(var i = 0; i < liTags.length; i++){
    liTags[i].onclick = function(){
        console.log(i) // 点击第3个 li 时,打印 2 还是打印 6?
    }
}
// 打印 6
// 当你点击时for早就遍历完,i 已经自增到 6

闭包 -MDN-方应杭

上一篇下一篇

猜你喜欢

热点阅读