函数声明
函数声明比变量声明具有更为优先的执行顺序,即我们常常提到的函数声明提前。因此我们在执行上下文中,无论在什么位置声明了函数,我们都可以在同一个执行上下文中直接使用该函数。
fn(); // function
function fn() {
console.log('function');
}
函数表达式
函数表达式使用了var进行声明,按照var的规则进行判断,即变量声明。
用var进行变量声明分两步:
// 变量声明
var a = 20;
// 实际执行顺序
var a = undefined; // 变量声明,初始值undefined,变量提升,提升顺序次于function声明
a = 20; // 变量赋值,该操作不会提升
同样,用变量声明的方式声明函数,也分两步:
fn(); // 报错
var fn = function() {
console.log('function');
}
执行顺序是:
var fn = undefined; // 变量声明提升
fn(); // 执行报错
fn = function() { // 赋值操作,此时将后边函数的引用赋值给fn
console.log('function');
}
这两种形式的函数在使用上并无不同。
在构造函数中添加方法
function Person(name) {
this.name = name;
this.age = age;
// 在构造函数内部中添加方法
this.getAge = function() {
return this.age;
}
this.
}
// 给原型添加方法
Person.prototype.getName = function() {
return this.name;
}
// 在对象中添加方法
var a = {
m: 20,
getM: function() {
return this.m;
}
}
匿名函数
就是指的没有被显示进行赋值操作的函数。它的使用场景,多作为一个参数传入另一个函数中。
var a = 10;
var fn = function(bar, num) {
return bar() + num;
}
fn(function() { //这里为匿名函数作为fn的参数
return a;
}, 20)
匿名函数的闭包
function() {
// 私有变量
var age = 20;
var name = 'Tom';
// 私有方法
function getName() {
return `your name is ` + name;
}
// 共有方法
function getAge() {
return age;
}
// 将引用保存在外部执行环境的变量中,形成闭包,防止该执行环境被垃圾回收
window.getAge = getAge;
})();
Jquery里利用模块和闭包
// 使用函数自执行的方式创建模块
(function(window, undefined) {
// 声明jQuery构造函数
var jQuery = function(name) {
// 主动在构造函数中,返回一个jQuery实例
return new jQuery.fn.init(name);
}
// 添加原型方法
jQuery.prototype = jQuery.fn = {
constructor: jQuery,
init:function() { ... },
css: function() { ... }
}
jQuery.fn.init.prototype = jQuery.fn;
// 将jQuery改名为$,并将引用保存在window上,形成闭包,对外开发jQuery构造函数,这样我们就可以访问所有挂载在jQuery原型上的方法了
window.jQuery = window.$ = jQuery;
})(window);
// 在使用时,我们直接执行了构造函数,因为在jQuery的构造函数中通过一些手段,返回的是jQuery的实例,所以我们就不用再每次用的时候在自己new了
$('#div1');
函数参数传递:按值传递
基本数据类型和引用数据类型的复制
var a = 20;
var b = a;
b = 10;
console.log(a); // 20
var m = { a: 1, b: 2 }
var n = m;
n.a = 5;
console.log(m.a) // 5
值传递:赋值栈中的数据,互不影响。
引用数据类型传递:复制栈中地址,指向堆中同一数据,一个改变,另一个也改变
var a = 20;
function fn(a) {
a = a + 10;
return a;
}
fn(a);
console.log(a); // 20
var a = { m: 10, n: 20 }
function fn(a) {
a.m = 20;
return a;
}
fn(a);
console.log(a); // { m: 20, n: 20 }
到底是引用传递还是值传递?
var person = {
name: 'Nicholas',
age: 20
}
function setName(obj) { // 传入一个引用
obj = {}; // 将传入的引用指向另外的值
obj.name = 'Greg'; // 修改引用的name属性
}
setName(person);
console.log(person.name); // Nicholas 未被改变
因为把{}复制给了obj,所以改变的是obj的name值,而不是person中的name
函数式编程
例如我们现在有一个数组,array = [1, 3, ‘h’, 5, ‘m’, ‘4’],现在想要找出这个数组中的所有类型为number的子项。当我们使用命令式编程思维时,可能就会直接这样做。
var array = [1, 3, 'h', 5, 'm', '4'];
var res = [];
for(var i = 0; i < array.length; i ++) {
if (typeof array[i] === 'number') {
res.push(array[i]);
}
}
而函数式编程的思维则建议我们将这种会多次出现的功能封装起来以备调用。
function getNumbers(array) {
var res = [];
array.forEach(function(item) {
if (typeof item === 'number') {
res.push(item);
}
})
return res;
}
// 以上是我们的封装,以下是功能实现
var array = [1, 3, 'h', 5, 'm', '4'];
var res = getNumbers(array);
var ele = document.querySelector('.test');
function setBackgroundColor(color) {
ele.style.backgroundColor = color;
}
// 多处使用
setBackgroundColor('red');
setBackgroundColor('#ccc');
良好的习惯如下
function setBackgroundColor(ele, color) {
ele.style.backgroundColor = color;
return color;
}
// 多处使用
var ele = document.querySelector('.test');
setBackgroundColor(ele, 'red');
setBackgroundColor(ele, '#ccc');
纯函数
相同的输入总会得到相同的输出,并且不会产生副作用的函数,就是纯函数。
所谓"副作用"(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
有些函数改变了原来局部变量的值
function getLast(arr) {
return arr[arr.length];
}
function getLast_(arr) {
return arr.pop();
}
var source = [1, 2, 3, 4];
var last = getLast(source); // 返回结果4 原数组不变
var last_ = getLast_(source); // 返回结果4 原数据最后一项被删除
一些纯函数和不纯函数
var source = [1, 2, 3, 4, 5];
source.slice(1, 3); // 纯函数 返回[2, 3] source不变
source.splice(1, 3); // 不纯的 返回[2, 3, 4] source被改变
source.pop(); // 不纯的
source.push(6); // 不纯的
source.shift(); // 不纯的
source.unshift(1); // 不纯的
source.reverse(); // 不纯的
// 我也不能短时间知道现在source被改变成了什么样子,干脆重新约定一下
source = [1, 2, 3, 4, 5];
source.concat([6, 7]); // 纯函数 返回[1, 2, 3, 4, 5, 6, 7] source不变
source.join('-'); // 纯函数 返回1-2-3-4-5 source不变