你真的懂函数吗?

函数声明方式

匿名函数

function后面直接跟括号,中间没有函数名的就是匿名函数。

let fn = function() {console.log('我是fn')
}
let fn2 = fn
console.log(fn.name) //fn
console.log(fn2.name)//fn,fn和fn2指向的是同一个function。
复制代码

具名函数

function后面有函数名字的,不是直接跟括号的的就是具名函数。 如果把一个具名函数赋值给一个变量,那么这个具名函数的作用域就不是window了。

let fn = function fn1() {console.log('function')
}
console.log(fn.name) //fn1
console.log(fn1,name) // ReferenceError: fn1 is not defined
复制代码

箭头函数

箭头函数是es6知识点,具有以下几个特点:

  1. 如果只有一个参数,可以省略小括号。
  2. 如果有至少有两个参数,必须加小括号。
  3. 如果函数体只有一句话可以省略花括号,并且这一句作为返回值return。
  4. 如果函数体至少有两句必须加上花括号。
  5. 箭头函数里面是没有this的。
let fn = e => e+1
console.log(fn(1)) //2let fn1 = (i,y) => i+y
console.log(fn1(2,3)) //5let fn2 = (i,y) => {i+=1;y+=2;return i+y
}
console.log(fn2(5,6)) //13
复制代码

词法作用域(静态作用域)

静态作用域又叫做词法作用域,采用词法作用域的变量叫词法变量。词法变量有一个在编译时静态确定的作用域。词法变量的作用域可以是一个函数或一段代码,该变量在这段代码区域内可见(visibility);在这段区域以外该变量不可见(或无法访问)。词法作用域里,取变量的值时,会检查函数定义时的文本环境,捕捉函数定义时对该变量的绑定。

词法作用域:变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。 with和eval除外,所以只能说JS的作用域机制非常接近词法作用域(Lexical scope)。

通过词法作用域树能判断变量指向关系,但是不能断定变量的值,变量的值还需要根据执行顺序进一步作出判断,看一下例子:

因为JavaScript采用的是词法作用域,bian'liang的作用域基于函数创建的位置,跟调用时的位置无关。

var i = 1,j = 2,k = 3;function a(o, p, x, q) {var x = 4;alert(i);function b(r, s) {var i = 11,y = 5;alert(i);function c(t) {var z = 6;alert(i);};var d = function() {alert(y);};c(60);d();};b(40, 50);
}
a(10, 20, 30); //1 11 11 5
复制代码
/**
* 模拟建立一棵语法分析树,存储function内的变量和方法
*/
var SyntaxTree = {// 全局对象在语法分析树中的表示window: {variables:{i:{ value:1},j:{ value:2},k:{ value:3}},functions:{a: this.a}},a:{variables:{x:'undefined'},functions:{b: this.b},scope: this.window},b:{variables:{i:'undefined'y:'undefined'},functions:{c: this.c,d: this.d},scope: this.a},c:{variables:{z:'undefined'},functions:{},scope: this.b},d:{variables:{},functions:{},scope: {scope: this.b}}
};
复制代码
/**
* 活动对象:函数执行时创建的活动对象列表
*/
let ActiveObject = {window: {variables: {i: { value: 1 }j: { value: 2 }k: { value: 3 }},functions: {a: this.a}}a: {variables: {x: { vale: 4 },functions: {b: this.b},scope: this.window,params: {o: { value: 10 },p: { value: 20 },x: this.variables.xq: { vale: 'undefined' }},arguments: [this.params.o, this.params.p, this.params.x]}}b: {variables: {i: { vale: 11 },y: { vale: 5 },},functions: {c: this.c,d: this.d},params: {r: { value: 40 }s: { value: 50 }},arguments: [this.params.r, this.params.scope]scope: this.a}c: {variables: {z: { value: 6 },functions: {},params: {t: { value: 60 }},arguments: [this.params.t]scope: this.b}}d: {variables: {},functions: {},params: {},arguments: []this.scope: this.b}}
复制代码

call stack

进入call stack 时的一些规则:

  1. 函数的所有形参(如果我们是在函数执行上下文中)

    • 由名称和对应值组成的一个变量对象的属性被创建;没有传递对应参数的话,那么由名称和 undefined 值组成的一种变量对象的属性也将被创建。
  2. 所有函数声明(FunctionDeclaration, FD)

    • 由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建;如果变量对象已经存在相同名称的属性,则完全替换这个属性。
  3. 所有变量声明(var, VariableDeclaration)

    • 由名称和对应值(undefined)组成一个变量对象的属性被创建;如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。
/*
* example1:形参
*/
function test(a, b) {/*var a = 10var b = undefined根据规则1,在进入执行上下文时会自动对形参声明并且赋值。*/ console.log(a)var c = 10;function d() {}var e = function _e() {};(function x() {});
}
test(10); // 10
复制代码
/*
* example2:函数声明
*/
function test(a, b) {console.log(a)function a() {}var e = function _e() {};
}
test(10); // ƒ a() {} .根据规则2,进入执行上下文会自动声明形参并且赋值,但是同名的函数声明会替换这个变量。function test(a, b) {console.log(a)var a = 30;var a = function _e() {};
}
test(10); // 10 .根据规则2,进入执行上下文会自动声明形参并且赋值,但是同名的函数声明会替换这个变量。
复制代码
/*
* example3:变量声明
*/
console.log(foo);//会打印出foo函数,根据规则3,同名的变量声明不会干扰函数声明和形参function foo(){console.log("foo");
}var foo = 1;
复制代码

this和arguments

函数调用

在es5中,函数有四种调用方式:

1. fn(p1,p2)
2. obj.fn(p1,p2)
3. fn.call(context,p1,p2)
4. fn.apply(context,p1,p2)
复制代码

第三和第四种才是正常的js函数调用方式,其他两种就是语法糖。

fn(p1,p2)     等价于 fn.call(undefined,p1,p2) 等价于 fn.apply(context,[p1,p2])
obj.fn(p1,p2) 等价于 obj.fn.call(obj,p1,p2)   等价于 obj.fn.apply(obj,[p1,p2])
复制代码

如果你传的 context 就 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)

this是什么??

this是call的第一个参数!!!!

var obj = {foo: function(){console.log(this)}
}var bar = obj.foo
obj.foo() // 打印出的 this 是 obj
bar() // 打印出的 this 是 window
复制代码
obj.foo() 相当于 obj.foo.call(obj) 也就相当于把函数名前面的作为call的第一个参数,也就是this,如果没有就是window。
bar() 相当于 bar.call(undefined) 
复制代码

在执行函数的时候,this是隐藏的一个参数,且必须是一个对象,如果不是,js是自动把它转为对象。

function fn() {console.log(this)console.log(arguments)
}
fn.call(1,2,3) // Number {1}  [2,3]
复制代码

arguments

arguments是伪数组它类似于Array,但除了length属性和索引元素之外没有任何Array属性。 call和apply里面除了第一个参数之外的都是arguments,如果arguments的个数少建议使用call,使用apply也可以,如果不确定就使用apply。 使用一下方法吧arguments转为真正的数组:

var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);// ES2015
const args = Array.from(arguments);
const args = [...arguments]
复制代码

bind

MDN 官方文档对 bind() 的定义:

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

大概意思就是,bind会返回一个新的函数(并没有的调用原来的函数),这个新函数会call原来的函数,call的参数由你决定。看例子:

		this.x = 9;var module = {x: 81,getX: function() { return this.x; }};var retrieveX = module.getX;var boundGetX = retrieveX.bind(module);boundGetX(); // 81
复制代码

retrieveX.bind(module)返回了一个新的函数boundGetX,然后调用这个新的函数的时候,把这个函数里面的this绑定到了module对象上,所以this.x就相当于module.x也就是等于81.

柯里化

在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个技术由克里斯托弗·斯特雷奇以逻辑学家哈斯凯尔·加里命名的,尽管它是Moses Schönfinkel和戈特洛布·弗雷格发明的。

说的明白一点就是,给函数传递一部分参数,让它返回一个函数去处理其他参数,举个例子,求三个数之和:

let addOne = function add(x) {return function(y) {return function(z) {return x+y+z}}
}let one = addOne(3)
console.log(one)//ƒ (y) {return function (z) {return x + y + z}}
let two = one(4)
console.log(two)//ƒ (z) {return x + y + z}
let three = two(5)
console.log(three)//12
复制代码

javascript函数柯里化--详细说明链接

高阶函数

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

  1. 接受一个或多个函数作为输入
  2. 输出一个函数

举一些高阶函数的例子:

/*
*接受一个或多个函数作为输入
*/
1. Array.prototype.filter()
2. Array.prototype.forEach()
3. Array.prototype.reduce()
4. Array.prototype.map()
5. Array.prototype.find()
6. Array.prototype.every()
复制代码
/*
*输出一个函数
*/
1. fn.bind(args)
复制代码

回调函数

函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。

名词形式:被当做参数的函数就是回调 动词形式:调用这个回调 注意回调跟异步没有任何关系

回调函数的使用场合

  1. 资源加载:动态加载js文件后执行回调,加载iframe后执行回调,ajax操作回调,图片加载完成执行回调,AJAX等等。

  2. DOM事件及Node.js事件基于回调机制(Node.js回调可能会出现多层回调嵌套的问题)。

  3. setTimeout的延迟时间为0,这个hack经常被用到,settimeout调用的函数其实就是一个callback的体现

  4. 链式调用:链式调用的时候,在赋值器(setter)方法中(或者本身没有返回值的方法中)很容易实现链式调用,而取值器(getter)相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是this指针,如果要实现链式方法,可以用回调函数来实现。

  5. setTimeout、setInterval的函数调用得到其返回值。由于两个函数都是异步的,即:他们的调用时序和程序的主流程是相对独立的,所以没有办法在主体里面等待它们的返回值,它们被打开的时候程序也不会停下来等待,否则也就失去了setTimeout及setInterval的意义了,所以用return已经没有意义,只能使用callback。callback的意义在于将timer执行的结果通知给代理函数进行及时处理。

回调函数的传递

传递的方式有两种,函数引用和函数表达式。

$.get('myhtmlpage.html', myCallBack);//这是对的
$.get('myhtmlpage.html', myCallBack('foo', 'bar'));//这是错的,那么要带参数呢?
$.get('myhtmlpage.html', function(){//带参数的使用函数表达式
myCallBack('foo', 'bar');
});
复制代码

箭头函数与es5的函数主要区别

箭头函数的主要区别在this,箭头函数是没有this这个概念的,看例子:

setTimeout(function(a){console.log(this) //这个this指的是{name:'Jack'}setTimeout(function(a){console.log(this) //这个this指的是window,因为没有bind,调用setTimeout的是window},1000)
}.bind({name:'Jack'}),1000)
复制代码
setTimeout(function(a){console.log(this) //这个this指的是{name:'Jack'}setTimeout(function(a){console.log(this) //这个this指的是{name: "Jack"},因为bind了外面的this也就是{name: "Jack"}},1000)
}.bind({name:'Jack'}),1000)
复制代码
setTimeout(function(a){console.log(this) //这个this指的是{name:'Jack'}setTimeout(a=>console.log(this),1000)//这个this指的是{name:'Jack'},因为箭头函数没有this的概念,它指的this就是外面的this,也就是{name:'Jack'}
}.bind({name:'Jack'}),1000)
复制代码

至此基本上说了js的所有函数内容,只是简单举个例子,更深入的研究还需要看一些其他大佬的博客哦~~~~

转载于:https://juejin.im/post/5aa5ad0af265da237a4ca4f5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/452418.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

静态html的ajax如何发请求,静态页面ajax - 冥焱的个人空间 - OSCHINA - 中文开源技术交流社区...

1.静态页面$.ajax({type:"get",url:"http://localhost:8080/app/register/sendSMS",//请求地址必须带http协议data:{"phone":phone},async:false,//是否异步dataType: "jsonp",//固定格式jsonp: "callback",//固定格式jsonp…

Diango博客--12.开发 Django 博客文章阅读量统计功能

文章目录0.models中增加新字段1.models中增加方法2.迁移数据库3.修改视图函数4.在模板中显示阅读量0.models中增加新字段 为了记录文章的浏览量,需要在文章的数据库表中新增一个用于存储阅读量的字段。 文件位置:blog/models.py class Post(models.Mo…

c++ try_catch throw

使用throw抛出异常 本人节选自《21天学通C》一书 抛出异常(也称为抛弃异常)即检测是否产生异常,在C中,其采用throw语句来实现,如果检测到产生异常,则抛出异常。该语句的格式为: throw 表达式…

数字证书和数字签名

什么是数字证书?由于Internet网电子商务系统技术使在网上购物的顾客能够极其方便轻松地获得商家和企业的信息,但同时也增加了对某些敏感或有价值的数据被滥用的风险. 为了保证互联网上电子交易及支付的安全性,保密性等,防范交易及支付过程中的欺诈行为&a…

域名劫持

转载于:https://www.cnblogs.com/xinghen1216/p/8548323.html

cesium html源码,Cesium源码的本地运行及调试

CesiumJS源码运行有两种方式:基于node.js运行官方下载地址:https://cesium.com/cesiumjs/下载解压后,在根目录安装依赖后,就可直接运行npm initnpm start如果调试代码呢,官方的示例都是在Sandcastle里放着,…

Diango博客--13.将“视图函数”类转化为“类视图”

文章目录0.思路引导1.ListView2.将 index 视图函数改写为类视图3.将 category 视图函数改写为类视图4.将 archive 视图函数改写成类视图5.将 tag 视图函数改写成类视图6.DetailView7.将DetailView视图函数改写成类视图0.思路引导 1)在开发网站的过程中,…

es6之数据结构 set,WeakSet,mapWeakMap

{let list new Set();list.add(1);list.add(2);list.add(1);console.log(list); //Set(2) {1, 2} let arr[1,2,3,1,2] let list2new Set(arr); console.log(list2); //Set(3) {1, 2, 3} } Set ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的&a…

关于C语言中继承和多态的实现

以下的内容综合了多篇文章,加上一点自己的理解而成。目的为了给自己阅读他们文章后做一个笔记。在末尾给出了这些文章的地址。 多态的实现可以采用以下几种方式: (1)使用 vod * (万能指针)来实现“编译时多态”。 (2)使用函数指针来实现“运行时…

边界测试

边界条件边界测试是单元测试中最后的也可能是最重要的任务。 软件常常在它的边界上失效,例如,处理n元数组的第n个元素时,或做到i次循环中的第i次重复时,往往会发生错误。 使用刚好小于、刚好等于和刚好大于最大值或最小值的数据结…

阿里云上Kubernetes集群联邦

摘要: kubernetes集群让您能够方便的部署管理运维容器化的应用。但是实际情况中经常遇到的一些问题,就是单个集群通常无法跨单个云厂商的多个Region,更不用说支持跨跨域不同的云厂商。这样会给企业带来一些担忧,如何应对可用区级别…

缺氧游戏计算机,缺氧PC最低什么配置一览 你觉得高吗

缺氧PC最低什么配置一览,你觉得高吗。游戏对于电脑有不同程度的要求,缺氧这款游戏也有着自己的配置要求,看看下面的缺氧PC最低什么配置一览,你的硬件够得上吗。缺氧最低配置:首先公布的是官 方配置需求,目前…

Diango博客--14.使用 Django 项目中的 ORM 编写伪造测试数据脚本

文章目录0.思路引导1.脚本目录结构2.使用 Faker 快速生成测试数据3.批量生成测试数据4.执行脚本5.效果展示0.思路引导 1)为了防止博客首页展示的文章过多以及提升加载速度,可以对文章列表进行分页展示。 2)不过这需要比较多的文章才能达到分…

基于Sql Server 2008的分布式数据库的实践

配置Sql Server 2008(Win7) 1.打开SQL server2012,使用windows身份登录 2.登录后,右键选择“属性”。左侧选择“安全性”,选中右侧的“SQL Server 和 Windows 身份验证模式”以启用混合登录模式 3.选择“连接”&#x…

横向技术分析C#、C++和Java优劣

本文将从技术人员的角度横向分析C#、C和Java优劣,其实选择Java阵营还是.NET阵营,大家可以根据自己的实际需要来确定。 C#诞生之日起,关于C#与Java之间的论战便此起彼伏,至今不辍。抛却Microsoft与Sun之间的恩怨与口角,…

软件测试中的存根程序

存根程序用来代替被测试的模块所调用的模块,因此存根程序也称为“虚拟子程序”,它利用被它代替的模块的接口,只做尽可能少的数据操作。

计算机网络应用云计算,计算机网络云计算的类型

原标题:计算机网络云计算的类型随着现代计算机网络技术的不断发展,越来越多的与计算机网络有关的现代化技术得以出现,并且有着广泛的应用,其中云计算技术就是比较常见的一种,在实际应用中发挥着较高的价值。在信息时代…

sublime_text快捷键

1、注释:选中文本后,CTRL / 2、CTRL N,CTRLS,保存成.html文件后,只需要输入感叹号!,然后tab键,即可打印出基本的html格式!转载于:https://www.cnblogs.com/JAVA-STUDYER/p/855040…

Diango博客--15.通过 Django Pagination 实现简单分页(一)

文章目录0.思路引导1.Paginator 类的常用方法2.用 Paginator 给文章列表分页3.在模板中设置分页导航4.效果展示0.思路引导 1)当博客上发布的文章越来越多时,通常需要进行分页显示,以免所有的文章都堆积在一个页面,影响用户体验。…

SpringMVC 测试 mockMVC

SpringMVC测试框架 基于RESTful风格的SpringMVC的测试,我们可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。 一 MockMvcBuilder MockMvcBuilder是用来构造MockMvc的构造器,其主要有两个实现&…