JavaScript 函数用法详解【函数定义、参数、绑定、作用域、闭包等】


Posted in Javascript onMay 12, 2020

本文实例讲述了JavaScript 函数用法。分享给大家供大家参考,具体如下:

初始函数

Function类型,即函数的类型。

典型的JavaScript函数定义:

function 函数名称(参数表){
  //函数执行部分
  return ;
}
//注意:参数列表直接写形参名即可

return语句:return返回函数的返回值并结束函数运行
函数也可以看做数据来进行传递
参数列表相当于函数入口return 语句相当于函数出口

函数可以作为参数来传递。

function test ( a ) {
    a();
  }
  test(function () {
    alert(1);
  });

函数可以嵌套定义

function test2(){
  function test3(){
    alert(1);
  }
  test3();
}
test2();

定义函数

三种定义函数的方式:

function语句形式
函数直接量形式
通过Function构造函数形式定义函数

//1 function 语句式
function test1 () {
  alert(1);
}

//2 函数直接量 (ECMAScript 推荐)

var test2 = function () {
  alert(2);
}


//3 function 构造函数式
var test3 = new Function('a','b','return a+b;'); //最后一个参数是函数结构体 
test3(10,20);
function语句 Function构造函数 函数直接量
兼容 完全 js1.1以上 js1.2以上版本
形式 句子 表达式 表达式
名称 有名 匿名 匿名
性质 静态 动态 静态
解析时机 优先解析 顺序解析 顺序解析
作用域 具有函数的作用域 顶级函数(顶级作用域) 具有函数作用域

静态动态的区别

var d1 = new Date();
var t1 = d1.getTime();

for ( var i=0; i<10000000; i++ ) {
  
//        function test1 () {} // 342ms //在函数体只会被声明一次 ,其它地方并不再解析,就可以调用 。 //静态 //只会编译一次,放入内存中。

  var test2 = function () {} //354ms
    
//          var test3 = new Function(); //8400ms //每次执行,都是重新new一个 函数。 //new 完之后, 就销毁了。不占用内存。动态创建一次。
  
}
var d2 = new Date();
var t2 = d2.getTime();

console.log( t2 - d1 );

解析顺序

function f () {
  return 1;
}         // 函数1

alert( f() );    //返回值为4 说明第1个函数被第4个函数覆盖

var f = new Function("return 2;");    // 函数2 

alert( f() );    //返回值为2 说明第4个函数被第2个函数覆盖

var f = function () { 
  return 3; 
}      // 函数3

alert( f() );      //返回值为3 说明第2个函数被第3个函数覆盖

function f () { 
  return 4; 
}         // 函数4

alert(f());    //返回值为3 说明第4个函数被第3个函数覆盖

var f = new Function("return 5");     // 函数5 

alert(f());  //返回值为5 说明第3个函数被第5个函数覆盖  

var f = function(){
  return 6;
}      // 函数6

alert(f());    //返回值为6 说明第5个函数被第6个函数覆盖

函数作用域

var k = 1 ; 
function t1(){
  var k = 2 ; 
//  function test(){return k ;} //2
// var test = function(){ return k}; //2
  var test = new Function('return k;'); //1  //new Function(); 是会有顶级作用域
  alert(test());
}
t1();

函数的参数arguments

arguments对象,是实参的副本

//js 中 函数分为 : 形参,实参
function test ( a,b,c,d ) {
  
//        console.log( test.length ); //获取形参的个数 4
  
  //函数的实际参数 内部就是使用一个数组去接收实际参数。 类数组对象
  //arguments 对象,只能在函数内部使用。
  //arguments 对象, 可以访问函数的实际参数 (实参的副本)
//        console.log( arguments.length ); //2
//        console.log( arguments[0] ); //10
//        // 第一种方式:
//        if ( test.length === arguments.length ) {
//          
//          return a + b;
//          
//        }
    
    //使用第二种 方式: 
    if ( arguments.callee.length === arguments.length ) {
      
      return a + b;
      
    }
  
  //arguments对象, 使用得最多的还是使用递归操作
//        arguments.callee; // 指向函数本身,函数体
  
}

test(10,20);

this对象的简单理解

this对象是在运行时基于函数的执行环境绑定的

在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象。

也就是说this关键字总是指代调用者(谁调用了我,就指向谁)。

//this: this对象是指在运行时期基于执行环境所绑定的

var k = 10;

function test () {
  
  this.k = 20;
  
}

test();

console.log( test.k ); //undefined

call和apply方法

每一个函数都包含两个非继承而来的方法:call、apply。这俩个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

call、apply的用途之一就是传递参数,但事实上,它们真正强大的地方式能够扩充函数赖以运行的作用域(使你的作用域不断的去变化)。

使用call()、aplly()来扩充作用域的最大好处就是对象不需要与方法有任何耦合关系

fn.call(obj);
让fn以运行,并且fn中的this以obj身份运行

将一个函数绑定到一个特定的作用域中,然后传递特定作用域中的参数。

//call, apply 简单 用法: 绑定一些函数 用于传递参数 调用

function sum ( x,y ) {
  
  return x + y;
  
}

function call1 ( num1,num2 ) {
  
  return sum.call(this,num1,num2);
  
}

function apply1 ( num1,num2 ) {
  
  return sum.apply(this,[num1,num2])
  
}

//将一个函数绑定到一个特定的作用域中,然后传递特定作用域中的参数。

console.log( call1(10,10) );
console.log( apply1(20,10) );

//扩充作用域,底层也经常使用这两个方法,用于绑定不同的作用域。
//把一个函数赋给一个对象, 赋完之后,还可以重用,赋给另外一个对象。

window.color = 'pink';

var obj = {
  color: 'tan'
}

function showColor () {
  
  console.log( this.color );
  
}

showColor.call(window); 
//        showColor.call(this);
showColor.apply(obj);

call方法简单的实现

function test1 ( a,b ) {
  
  return a + b;
  
}

//自定义对象
function Obj ( x,y ) {
  
  return x * y;
  
}

var obj = new Obj();

//挂载到对象上
obj.method = test1;

//执行该函数
obj.method(10,20);

//执行完后删除
delete obj.method;

bind

ES5中提供一个bind()方法。
为函数绑定一个执行时候的作用域。
将该方法绑定Function的原型上,因此定义一个function就有该方法,bind()添加作用域的时候,方法没有执行,在方法执行的时候,作用域会变成绑定兑现的作用域。

function b () {
  console.log(this.title);
}

function Book ( title ) {
  this.title = title;
}

var book = new Book('javascript');

// apply , call 特点:调用就执行
//      b.call(book);
//      b.apply(book);

// 当执行一个函数的时候更改作用域 
var bBindfn = b.bind(book);

// 执行更改作用域
bBindfn();

执行环境和作用域链概念

执行环境(execution context)是javascript中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每一个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们的代码无法访问这个对象,但是解析器在处理数据时会在后台执行它。

JavaScript 函数用法详解【函数定义、参数、绑定、作用域、闭包等】

全局执行环境是最外围的一个执行环境。根据ECMScript实现所在的宿主环境不同,表示执行环境的对象也不一样。

每一个函数都有自己的执行环境。当执行流进一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返还给之前的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问(控制代码的访问权限)。

var color1 = "blue";

function changeColor () {
  
  var color2 = "red";

  function swapColor () {
    
    var color3 = color2; //color3 = 'red'
    color2 = color1; //color2 = 'blue'
    color1 = color3; //color1 = 'red'
    
    console.log( color1,color2,color3 ); 
    
  }
  
  swapColor();
  
}

changeColor();

//环境变量 可以一层一层的向上进行追溯 可以访问它的上级 环境(变量和函数)
// 作用域链 具有层级关系
//在大型程序中,全局变量,尽量少使用,因为全局变量总是最后一次搜索。 防止全局变量污染。//很少去定义全局变量,效率比较慢。

垃圾收集和块级作用域的概念

垃圾收集

javascript是一门具有自动垃圾收集机制的编程语言。开发人员不必关心内存分配和回收问题。

垃圾回收器也是每隔一段时间去进行回收。

离开作用域的值将被自动标记为可以回收,因此将在垃圾收集期间被删除。标记清除是目前主流的垃圾收集算法。这种思想是给当前不使用的值加上标记,然后回收其内存。

//垃圾收集 ,标记清除 (模拟)

function test () {
  
  var a = 10;  //mark - 被使用
  var b = 20;  //mark - 被使用
  
}

test(); //执行完毕 之后 ,a,b又被标记使用。 mark - 没有被使用
//在间隔时间中 回收。 如果mark 没有被使用, 则回收。

//引用计数(模拟)
//如果变量被引用 , count = 1;
function test2 () {

  var a = 10;  //count = 1;
  var b = 20;

  var c;
  c = a; //count++ = 2; //a 被 c 所使用 ,引用。

  a = 50; //count--; //重新被赋值 count--  //等待 count 为 0 的时候, 垃圾回收机制 就回收

}

块级作用域

javascript里面没有块级作用域的概念,所以在使用if、for时候要格外的小心。

javascript模拟块级作用域 (块级作用域,相当于内部的执行体,一个执行环境)
利用 IIEF的特性

//当函数执行之后, 变量就被回收      
  
function test () {
  
  (function () { //函数有一个单独的作用域,外面无法访问到 i
    
    for ( var i=0; i<=5; i++ ) {
      
      console.log( i );
      
    }
    
  })();
  
  console.log( i ); //报错,无法找到
  
}

test();

闭包 Closure

闭包与函数有着紧密的关系,它是函数的代码在运行过程中的一个动态环境,是一个运行期的、动态的概念。

所谓闭包,是指词法表示包括不必计算的变量的函数。也就是说,该函数能够使用函数外定义的变量。

在程序语言中,所谓闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。这些外部执行域的非持久型变量神奇地保留它们在闭包最初定义(或创建)时的值

理解闭包,必须要对于作用域链的概念非常的清楚。

var name = "xiao A";

var obj = {
  
 name : "xiao B",
  
 getName: function(){
    
    return function(){
      
      return this.name;
      
    }
    
  }
  
};

console.log(obj.getName()()); //xiao A
//类似:
//var zf = obj.getName();//全局作用域
//zf();

var name = "xiao A";

var obj = {
  
 name : "xiao B",
  
 getName: function(){
    
  var self = this;  
    
    return function(){
      
      return self.name;
      
    }
    
  }
  
};

//console.log( obj.getName().call(obj) );
console.log( obj.getName()() );

//闭包: 一个函数, 可以访问另外一个作用域中的变量
//封闭性,(类似食品包装袋一样,封闭起来,保质期延长,变量的访问范围的延长) //private 起到一个保护变量的作用

//1 level 
function f(x){ //2 level
  
  var temp = x; //局部变量  //temp 标记 已经没有被使用
   
  return function(x){ //3 level  (function 有一个执行域)
    
    temp += x;  //temp 下一级作用域仍然被引用 , 标记为 使用
    
    alert(temp);
  
  }
  
}        

//js 垃圾回收机制,当函数执行完毕后,内部所有的局部变量都集体的被回收。

var a = f(50);

a(5); //55

a(10); //65

a(20); //85

回调函数

  1. 回调函数执行

  2. 回调函数中的this

  3. 回调函数的返回值

forEach

// forEach:用来遍历数组中的每一项
// 1. 数组中有几项,那么传递进去的匿名回调函数就需要执行几次。
// 2. 每一次执行匿名函数的时候,还传递了三个参数值:数组中的当前项item,当前项的索引index,原始的数组input
// forEach方法中的this是arr,匿名函数回调函数的this默认是window
var arr = [10, 234, 23, 76, 7666, 34];
arr.forEach(function(item, index, input) {
 input[index] = item * 10; // 操作之后,修改了原数组
 console.log(arguments);
});

var obj = {name: 'zf'};
// arr.forEach(function(item, index) {
//  console.log(this);
// }.call(obj)); // 给forEach赋值的是时候,首先把匿名函数执行,把匿名函数中的this变为obj,把匿名函数执行的返回结果undefined赋值给foreach

arr.forEach(function(item, index) {
 console.log(this, '---');
}.bind(obj)); // bind 只是预先处理,先把this转为参数的对象,到后续该执行的时候才执行。


// 不管是forEach,还是map都支持第二个参数,第二个参数表示:把匿名函数中的this进行修改。
arr.forEach(function() {
 console.log(this, 'this');
}, obj);

forEach兼容处理

// 兼容处理
Array.prototype._forEach = function(callback, context) {
 content = content || window;
 if ('forEach' in Array.prototype) {
  this.forEach(callback, context);
  return;
 } 
 // IE6-8,执行回调逻辑
 for (var i=0; i<this.length; i++) {
  callback || callback.call(context, this[i], i, this);
 }
}

map

var arr = [10, 234, 23, 76, 7666, 34];
arr.map(function(item, index, input) { // 原有数组不变
 console.log(arguments);
 return item * 10;
});
// map和forEach非常相似,都是用来遍历数组中的每一项的值
// 区别:map的回调函数中支持return返回值,return的是什么,相当于把数组中的这一项改变为什么(但是并不影响原来的数组,只是相当于把原数组克隆一份,把克隆的这一份的数组中的对应项改变)

map兼容处理

Array.prototype._map = function(callback, context) {
 context = context || window;
 if ('map' in Array.prototype) {
  this.map(callback, context);
  return;
 } 
 // IE6-8,执行回调逻辑
 var resArr = [];
 for (var i=0; i<this.length; i++) {
  if (typeof callback === 'function') {
   resArr[resArr.length] = callback.call(context, this[i], i, this);
  }
 }
 return resArr;
}

var arr = [10, 234, 23, 76, 7666, 34];
arrMap = arr._map(function(item, index, input) { // 原有数组不变
 // console.log(arguments, '_map');
 return item * 100;
});
console.log(arrMap);

柯理化函数思想

柯理化函数思想:一个JS预处理思想

核心:利用函数执行可以形成一个不销毁的私有作用域的原理,把需要预先处理的内容都存储在这个不销毁的作用域中,并且返回一个匿名函数,执行的都是匿名函数,把匿名函数中,把之前的预先存储的值进行相关操作处理即可。

var obj = {name: 'zf'};
function fn(num1, num2) {
 console.log(this, num1, num2)
}

// 把传递进来的callback这个方法中的this预先处理为context
function bind(callback, context) {
 context = context || window;
 var outArg = Array.prototype.slice.call(arguments, 2);
 return function(ev) {
  var innerArg = Array.prototype.slice.call(arguments, 0);
  callback.apply(context, outArg.concat(innerArg));
 }
}

// document.body.onclick = fn; // fn 中的 this是document.body. num1是 MouseEven对象
document.body.onclick = fn.bind(obj, 100, 200); // 除了预先处理了this和需要手动传递的参数值以外,把浏览器默认给传递的鼠标事件对象也进行预先处理,传递到最后一个参数。
document.body.onclick = bind(obj, 100, 200)
// document.body.onclick = function() {
 // console.log(this); // this --> document.body
// }

// window.setTimeout(fn.bind(obj), 0);
// window.setTimeout(bind(fn, obj, 100, 20), 0); // 给定时器绑定方法,然后定时器到达时间的时候,让fn执行,并且让fn中的this变为obj

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun测试上述代码运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
javascript firefox不显示本地预览图片问题的解决方法
Nov 12 Javascript
jquery remove方法应用详解
Nov 22 Javascript
用jquery中插件dialog实现弹框效果实例代码
Nov 15 Javascript
JavaScript插件化开发教程(五)
Feb 01 Javascript
跟我学习javascript的Date对象
Nov 19 Javascript
jQuery学习心得总结(必看篇)
Jun 10 Javascript
jQuery学习之DOM节点的插入方法总结
Jan 22 Javascript
element-ui循环显示radio控件信息的方法
Aug 24 Javascript
详解Vue3.0 前的 TypeScript 最佳入门实践
Jun 18 Javascript
vue v-for 使用问题整理小结
Aug 04 Javascript
原生js实现ajax请求和JSONP跨域请求操作示例
Mar 14 Javascript
鸿蒙系统中的 JS 开发框架
Sep 18 Javascript
JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】
May 12 #Javascript
JavaScript 接口原理与用法实例详解
May 12 #Javascript
ES5新增数组的实现方法
May 12 #Javascript
JavaScript内置对象之Array的使用小结
May 12 #Javascript
详解ES6新增字符串扩张方法includes()、startsWith()、endsWith()
May 12 #Javascript
Vue 中如何将函数作为 props 传递给组件的实现代码
May 12 #Javascript
详解ES6数组方法find()、findIndex()的总结
May 12 #Javascript
You might like
jquery一般方法介绍 入门参考
2011/06/21 Javascript
JS定时器实例
2013/04/17 Javascript
javascript实现焦点滚动图效果 具体方法
2013/06/24 Javascript
javascript瀑布流式图片懒加载实例
2020/06/28 Javascript
AngularJS延迟加载html template
2016/07/27 Javascript
highcharts 在angular中的使用示例代码
2017/09/20 Javascript
解决vue打包之后静态资源图片失效的问题
2018/02/21 Javascript
JS模拟实现哈希表及应用详解
2018/05/04 Javascript
D3.js实现拓扑图的示例代码
2018/06/30 Javascript
工作中常用到的ES6语法
2018/09/04 Javascript
JS闭包经典实例详解
2018/12/20 Javascript
解决vue语法会有延迟加载显现{{xxx}}的问题
2019/11/14 Javascript
jQuery 隐藏/显示效果函数用法实例分析
2020/05/20 jQuery
python执行get提交的方法
2015/04/29 Python
使用Python搭建虚拟环境的配置方法
2018/02/28 Python
python实现在pandas.DataFrame添加一行
2018/04/04 Python
Python中的几种矩阵乘法(小结)
2019/07/10 Python
对django后台admin下拉框进行过滤的实例
2019/07/26 Python
numpy中三维数组中加入元素后的位置详解
2019/11/28 Python
基于Python检测动态物体颜色过程解析
2019/12/04 Python
pytorch nn.Conv2d()中的padding以及输出大小方式
2020/01/10 Python
python GUI库图形界面开发之PyQt5计数器控件QSpinBox详细使用方法与实例
2020/02/28 Python
python 在右键菜单中加入复制目标文件的有效存放路径(单斜杠或者双反斜杠)
2020/04/08 Python
基于打开pycharm有带图片md文件卡死问题的解决
2020/04/24 Python
Python 捕获代码中所有异常的方法
2020/08/03 Python
Stuarts London美国/加拿大:世界领先的独立男装零售商之一
2019/03/18 全球购物
Yves Rocher捷克官方网站:植物化妆品的创造者
2019/07/31 全球购物
俄罗斯在线购买飞机票、火车票、巴士票网站:Tutu.ru
2020/03/16 全球购物
查询优化的一般准则有哪些
2015/03/08 面试题
工程造价专业大专生求职信
2013/10/06 职场文书
奥利奥广告词
2014/03/20 职场文书
房屋转让协议书范本
2014/04/11 职场文书
总经理岗位职责
2015/02/04 职场文书
2015中秋节晚会主持词
2015/07/01 职场文书
开学第一周日记(三篇范文)
2019/08/23 职场文书
浅谈Python中的函数(def)及参数传递操作
2021/05/25 Python