JavaScript的函数不但是“头等公民”,而且可以像变量一样使用,具有非常强大的抽象能力。
定义函数的方式如下:
function abs(x) {     if (x >= 0) {         return x;     } else {         return -x;     } }
如果没有return语句,函数执行完毕后也会返回结果,只是结果为undefined。
由于JavaScript的函数也是一个对象,上述定义的abs()函数实际上是一个函数对象,而函数名abs可以视为指向该函数的变量。
因此,第二种定义函数的方式如下:
var abs = function (x) {     if (x >= 0) {         return x;     } else {         return -x;     } };
在这种方式下,function (x) { ... }是一个匿名函数,它没有函数名。但是,这个匿名函数赋值给了变量abs,所以,通过变量abs就可以调用该函数。
述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。
abs(); // 返回NaN 
此时abs(x)函数的参数x将收到undefined,计算结果为NaN。
要避免收到undefined,可以对参数进行检查:
function abs(x) {     if (typeof x !== 'number') { throw 'Not a number';     }     if (x >= 0) {         return x;     } else {         return -x;     } }
请严格遵守“在函数内部首先申明所有变量”这一规则。最常见的做法是用一个var申明函数内部用到的所有变量:
function foo() {     var         x = 1, // x初始化为1         y = x + 1, // y初始化为2         z, i; // z和i为undefined // 其他语句: for (i=0; i<100; i++) {         ...     } }
全局作用域
不在任何函数内定义的变量就具有全局作用域。实际上,JavaScript默认有一个全局对象window,全局作用域的变量实际上被绑定到window的一个属性:
var course = 'Learn JavaScript'; alert(course); // 'Learn JavaScript' alert(window.course); // 'Learn JavaScript'
以变量方式var foo = function () {}定义的函数实际上也是一个全局变量,因此,顶层函数的定义也被视为一个全局变量,并绑定到window对象:
 function foo() {     alert('foo'); }  foo(); // 直接调用foo() window.foo(); // 通过window.foo()调用
不同的JavaScript文件如果使用了相同的全局变量,或者定义了相同名字的顶层函数,都会造成命名冲突,并且很难被发现。
减少冲突的一个方法是把自己的所有变量和函数全部绑定到一个全局变量中。例如:
// 唯一的全局变量MYAPP: var MYAPP = {};  // 其他变量: MYAPP.name = 'myapp'; MYAPP.version = 1.0;  // 其他函数: MYAPP.foo = function () {     return 'foo'; };
局部作用域
由于JavaScript的变量作用域实际上是函数内部,我们在for循环等语句块中是无法定义具有局部作用域的变量的:
'use strict';  function foo() {     for (var i=0; i<100; i++) {         //     }     i += 100; // 仍然可以引用变量i } 
为了解决块级作用域,ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量:
'use strict';  function foo() {     var sum = 0;     for (let i=0; i<100; i++) {         sum += i;     }     // SyntaxError:     i += 1; }
关键字const来定义常量
解构赋值,可以同时对一组变量进行赋值。
,     gender: 'male',     passport: 'G-12345678',     school: 'No.4 middle school',     address: {         city: 'Beijing',         street: 'No.1 Road',         zipcode: '100001'     } }; var {name, address: {city, zip}} = person; name; // '小明' city; // 'Beijing' zip; // undefined, 因为属性名是zipcode而不是zip // 注意: address不是变量,而是为了让city和zip获得嵌套的address对象的属性: address; // Uncaught ReferenceError: address is not defined
解构赋值还可以使用默认值,这样就避免了不存在的属性返回undefined的问题:
var person = {     name: '小明',     age: 20,     gender: 'male',     passport: 'G-12345678' };  // 如果person对象没有single属性,默认赋值为true: var {name, single=true} = person; name; // '小明' single; // true
有些时候,如果变量已经被声明了,再次赋值的时候,正确的写法也会报语法错误:
// 声明变量: var x, y; // 解构赋值: {x, y} = { name: '小明', x: 100, y: 200}; // 语法错误: Uncaught SyntaxError: Unexpected token = 
这是因为JavaScript引擎把{开头的语句当作了块处理,于是=不再合法。解决方法是用小括号括起来:
({x, y} = { name: '小明', x: 100, y: 200});
在一个对象中绑定函数,称为这个对象的方法。
var xiaoming = {     name: '小明',     birth: 1990,     age: function () {         var y = new Date().getFullYear();         return y - this.birth;     } };  xiaoming.age; // function xiaoming.age() xiaoming.age(); // 今年调用是25,明年调用就变成26了
让我们拆开写:
function getAge() {     var y = new Date().getFullYear();     return y - this.birth; }  var xiaoming = {     name: '小明',     birth: 1990,     age: getAge };  xiaoming.age(); // 25, 正常结果 getAge(); // NaN 
单独调用函数getAge()怎么返回了NaN?请注意,我们已经进入到了JavaScript的一个大坑里。
JavaScript的函数内部如果调用了this,那么这个this到底指向谁?
答案是,视情况而定!
如果以对象的方法形式调用,比如xiaoming.age(),该函数的this指向被调用的对象,也就是xiaoming,这是符合我们预期的。
如果单独调用函数,比如getAge(),此时,该函数的this指向全局对象,也就是window。
“创建一个匿名函数并立刻执行”的语法:
(function (x) {     return x * x; })(3); // 9
需要用括号把整个函数定义括起来
箭头函数
x => x * x 
上面的箭头函数相当于:
function (x) {     return x * x; }
相当于匿名函数,并且简化了函数定义。箭头函数有两种格式,一种像上面的,只包含一个表达式,连{ ... }和return都省略掉了。还有一种可以包含多条语句,这时候就不能省略{ ... }和return:
x => {     if (x > 0) {         return x * x;     }     else {         return - x * x;     } }
如果参数不是一个,就需要用括号()括起来:
// 两个参数: (x, y) => x * x + y * y
// 无参数: () => 3.14
如果要返回一个对象,需要加小括号
x => ({ foo: x })
Promise
异步执行可以用回调函数实现
AJAX就是典型的异步操作。
看一个最简单的Promise例子:生成一个0-2之间的随机数,如果小于1,则等待一段时间后返回成功,否则返回失败:
function test(resolve, reject) {     var timeOut = Math.random() * 2;     log('set timeout to: ' + timeOut + ' seconds.');     setTimeout(function () {         if (timeOut < 1) {             log('call resolve()...');             resolve('200 OK');         }         else {             log('call reject()...');             reject('timeout in ' + timeOut + ' seconds.');         }     }, timeOut * 1000); } 
这个test()函数有两个参数,这两个参数都是函数,如果执行成功,我们将调用resolve('200 OK'),如果执行失败,我们将调用reject('timeout in ' + timeOut + ' seconds.')。可以看出,test()函数只关心自身的逻辑,并不关心具体的resolve和reject将如何处理结果。
var p1 = new Promise(test); var p2 = p1.then(function (result) {     console.log('成功:' + result); }); var p3 = p2.catch(function (reason) {     console.log('失败:' + reason); });
变量p1是一个Promise对象,它负责执行test函数。由于test函数在内部是异步执行的,当test函数执行成功时,我们告诉Promise对象:
// 如果成功,执行这个函数: p1.then(function (result) {     console.log('成功:' + result); }); 
当test函数执行失败时,我们告诉Promise对象:
p2.catch(function (reason) {     console.log('失败:' + reason); });
Promise对象可以串联起来,所以上述代码可以简化为:
new Promise(test).then(function (result) {     console.log('成功:' + result); }).catch(function (reason) {     console.log('失败:' + reason); });
Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。
job1.then(job2).then(job3).catch(handleError);
其中,job1、job2和job3都是Promise对象。
// 0.5秒后返回input*input的计算结果:
function multiply(input) {
    return new Promise(function (resolve, reject) {
        log('calculating ' + input + ' x ' + input + '...');
        setTimeout(resolve, 500, input * input);
    });
}
// 0.5秒后返回input+input的计算结果:
function add(input) {
    return new Promise(function (resolve, reject) {
        log('calculating ' + input + ' + ' + input + '...');
        setTimeout(resolve, 500, input + input);
    });
}
var p = new Promise(function (resolve, reject) {
    log('start new Promise...');
    resolve(123);
});
p.then(multiply)
 .then(add)
 .then(multiply)
 .then(add)
 .then(function (result) {
    log('Got value: ' + result);
});
把上一节的AJAX异步执行函数转换为Promise对象,看看用Promise如何简化异步处理:
'use strict';  // ajax函数将返回Promise对象: function ajax(method, url, data) {     var request = new XMLHttpRequest();     return new Promise(function (resolve, reject) {         request.onreadystatechange = function () {             if (request.readyState === 4) {                 if (request.status === 200) {                     resolve(request.responseText);                 } else {                     reject(request.status);                 }             }         };         request.open(method, url);         request.send(data);     }); } 
var log = document.getElementById('test-promise-ajax-result');
var p = ajax('GET', '/api/categories');
p.then(function (text) { // 如果AJAX成功,获得响应内容
    log.innerText = text;
}).catch(function (status) { // 如果AJAX失败,获得响应代码
    log.innerText = 'ERROR: ' + status;
});
可以采用链式写法,即then方法后面再调用另一个then方法。
getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) {  // ... }); 
上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。
getJSON("/post/1.json").then(function(post) { return getJSON(post.commentURL); }).then(function funcA(comments) {   console.log("resolved: ", comments); }, function funcB(err){   console.log("rejected: ", err); }); 
上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用funcA,如果状态变为rejected,就调用funcB。
如果采用箭头函数,上面的代码可以写得更简洁。
getJSON("/post/1.json").then(   post => getJSON(post.commentURL) ).then(   comments => console.log("resolved: ", comments),   err => console.log("rejected: ", err) );
有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。
												
												
								- JS 异步系列 —— Promise 札记
		
Promise 研究 Promise 的动机大体有以下几点: 对其 api 的不熟悉以及对实现机制的好奇; 很多库(比如 fetch)是基于 Promise 封装的,那么要了解这些库的前置条件得先熟悉 ...
		 
						- 大话JS神器之Promise
		
前段时间的工作中,由于项目要在前端实现存储,于是便使用了websql,而websql的API涉及到了很多的异步问题,如果采取回调函数的方式处理,代码不够优雅,而且不利于理解,于是便找到了Promise ...
		 
						- react  request.js  函数封装
		
1.request.js  函数封装 import { Toast } from 'antd-mobile'; import axios from 'axios'; import store from ...
		 
						- JS中的Promise
		
Promise 对象有以下两个特点. (1)对象的状态不受外界影响.Promise 对象代表一个异步操作,有三种状态:Pending(进行中).Resolved(已完成,又称 Fulfilled)和  ...
		 
						- 如何编写高质量的 JS 函数(2) -- 命名/注释/鲁棒篇
		
本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/sd2oX0Z_cMY8_GvFg8pO4Q作者:杨昆 上篇<如何编写高质量的 JS 函数 ...
		 
						- JS 异步与 Promise
		
JS 异步与 Promise 本文写于 2020 年 6 月 8 日 1. 同步与异步与回调函数 Promise 现在是前端面试必考题呀,但是先不急着看 Promise,我们首先来看看什么是异步. - ...
		 
						- 3.3 js函数
		
1.函数语法: 函数声明的方式:function 函数名(参数1,参数2-){//函数体;}函数调用:函数名(参数1,参数2-); 函数内不一定都指定返回值. 如果需要指定返回值,可用 return  ...
		 
						- Js函数function基础理解
		
正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定 ...
		 
						- js函数表达式和函数声明的区别
		
我们已经知道,在任意代码片段外部添加包装函数,可以将内部的变量和函数定义"隐 藏"起来,外部作用域无法访问包装函数内部的任何内容. 例如: var a = 2; function  ...
		 
		
	
随机推荐
	
									- 阅读《Android 从入门到精通》(24)——切换图片
			
切换图片(ImageSwitcher) java.lang.Object; android.view.View; android.widget.ViewGroup; android.widget.Fr ...
			 
						- 定义JQuery插件
			
http://cavalry800528.iteye.com/blog/1953917 一:导言 有些WEB开发者,会引用一个JQuery类库,然后在网页上写一写$("#"),$( ...
			 
						- 算法笔记_154:算法提高 日期计算(Java)
			
目录 1 问题描述 2 解决方案   1 问题描述 问题描述 已知2011年11月11日是星期五,问YYYY年MM月DD日是星期几?注意考虑闰年的情况.尤其是逢百年不闰,逢400年闰的情况. 输入格式 ...
			 
						- SpringMVC请求参数和响应结果全局加密和解密
			
前提 前段时间在做一个对外的网关项目,涉及到加密和解密模块,这里详细分析解决方案和适用的场景.为了模拟真实的交互场景,先定制一下整个交互流程.第三方传输(包括请求和响应)数据报文包括三个部分: 1.t ...
			 
						- ant design pro(一)安装、目录结构、项目加载启动【原始、以及idea开发】
			
一.概述 1.1.脚手架概念 编程领域中的“脚手架(Scaffolding)”指的是能够快速搭建项目“骨架”的一类工具.例如大多数的React项目都有src,public,webpack配置文件等等, ...
			 
						- ubuntu——更新、编译、启动内核
			
步骤如下: 1.make mrproper Linux下面去编译项目之前,一般常会用make mrproper去先删除之前编译所生成的文件和配置文件,备份文件等,其中,mrproper和distcle ...
			 
						- Intellij IDEA 14使用maven3.3.3 问题
			
Intellij IDEA 14使用maven3.3.3报错: -Dmaven.multiModuleProjectDirectory system propery is not set. Check ...
			 
						- Android API之android.view.View.MeasureSpec
			
android.view.View.MeasureSpec MeasureSpec是View的内部类 public static class MeasureSpec MeasureSpec封装从par ...
			 
						- CSC时无法找到C:\Program Files\Microsoft SDKs\Windows\v6.0A\lib
			
偶然用到CSC编译c#程序,出现如下问题: warning CS1668: “LIB 环境变量”中指定的搜索路径“C:\Program Files\Microsoft        SDKs\Wind ...
			 
						- 阿里云 部署并开启nodejs应用
			
1.下载资源  $ wget https://nodejs.org/dist/v8.11.2/node-v8.11.2-linux-x64.tar.xz 2.xz解压 $ xz -d node-v8. ...