一、函数创建(函数定义)
1.函数初体验
函数的作用:代码的封装
1.函数需要先定义再使用;
2.函数的命名遵循驼峰命名发;
3.函数如果重名,后面的会覆盖前面的。
- 字面量方式
function声明(预解析)
function fn() {//...}
var 赋值表达式
var fn = function () { // ...}
-
构造函数
var fn = new Function(‘num1’,‘return num1’);
这一函数过程执行过程:
-
创建了一个新对象,开辟一个新的空间,存储创建出来的对象;
-
将this指向这个新对象
-
执行构造函数里面的代码
-
返回新对象(this)
-
方法
var obj = {};
obj.fn = function() {
//TODO
}
二、 递归
递归满足的条件
-
能够执行下一步
-
能够跳出循环
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
当时在严格模式下argeument.callee 并不能使用,可以零用表达式和匿名函数来解决:
var fs = (function test(num){
if(num <= 1){
return 1;
}else{
return num * test(num-1);
}
//下面这两行代码完全不会影响运行,不信?那你可以试试
//var f = test;
// test = null;
});
console.log(fs(2));
三、 函数声明位置
- if和for语句中 (不推荐这么写)
- 函数内部 (除了闭包,不推荐这么写 )
- 对象中的方法
四、函数调用
-
匿名函数调用
(function() {})();
-
函数间接调用
var name = ‘xm’;
var person = {
name: ‘xh’,
sayName: function() {
console.log(this.name);
}
}
person.sayName.call(this);
apply和call的区别是: call是pally的语法糖。(说的详细点,区别在于第个参数,apply第二个参数可以是数组或者类数组,二这个对象中的每个元素,都是一个参数,但call第二个参数只是传入一个参数)
function add(x,y) {
return x + y;
}
var arr = [1,2];
add.apply(this,arr);
bind 是es5提供的方法, 改变函数内部this的指向。
var obj = {
x: 1,
y: 2
}
function input() {
console.log(this.x+this.y);
}
input(); //NaN undefined + undefined
input.bind(obj)(); /
五、参数类型
- 参数类型
-
形参
-
实参
$.ajax({
url: request_url,
data: request_data,
type: ‘Get’,
success: function(result) {
handle(result);
}
})
handle(result) {
// TODO
}
- arguments
arguments是类数组。在我们定义了一个函数的时候,如果不缺定用户是否传入了参数,或者不知道用户是否传入了参数,无法进行计算。但是如果一直参数的个数和值,可以定义arguments对象,来获取函数调用的时候参入的参数。
function showargs() {
return [].slice.call(arguments);
}
showargs();
- 传参
- 当基本类型的变量作为实参,则不改变实参类型
- 当引用类型的变量作为实参,则改变其属性
- bind、call、apply
- bind 改变作用域,返回函数
- call 执行函数,可更改作用域、参数是以单个单个传入
- apply 执行函数,可更改作用域、参数是以数组传入
-
默认参数
function fn (arg) {
if (arg === undefined) arg = 5 // 默认参数
return arg
}
###6.caller属性
调用者,比如f1函数在f2函数中调用,此时f1的调用者就是f2。
六、返回函数
返回函数的标识是return,使用return的注意项:
-
当前函数执行return后,会跳出当前函数
function a() {
if(true) {
return;
}
console.log(‘test return after’);
}
function b() {
console.log(‘function b’);
}
function {
a();
b();
}
excue(); // 只输出了’function b’
利用这一特性,也可以跳出循环语句,下列模拟es6的数组的find()方法。
function find(arr,fn) {
for(var i = 0 , item; item = arr[i++];) {
var flag = fn(item,i);
if(flag) {
return item;
}
}
}
var str = find( [1,2,3] ,function(item,index){
return item > 3;
});
console.log(str);
- return、return false 和retrun true 的区别。
- retrun true; 返回正确的处理结果。
- return false;分会错误的处理结果,终止处理。
- return; 把控制权返回给页面。
七、 闭包
闭包 是指:有权访问 另一个函数 作用域中的变量的函数。
- 函数的预解析和变量问题
- 变量作用问题(参考js基础的作用域和上下文)
- 构造函数的使用 私有变量 和 私有方法,参考对象知识点
-
模拟块级作用
function box() {
var arr = [];
for (var i = 0; i < 5; i++) {
arr[i] = (function(num) {
return function() { //直接返回值,改 2 变成返回函数
return num; //原理和改 1一样
}
})(i);
}
return arr;
}
var b = box();
for (var i = 0; i < b.length; i++) {
console.log(‘匿名函数’,bi); //这里通过 bi函数调用即可
} -
this 问题
在这里只说一个问题,this针对的对象 永远是指在 调用时 使用的那个对象,在其他多数情况下,多数指 window,例如,把对象的方法作为值进行的变量赋值,则 this 可能就是指 window。
window.name = 'window';
var obj = {
name: 'liangcheng',
sayName: function() {
console.log('this',this.name);
}
};
var foo = obj.sayName;
foo(); // window
(obj.sayName = obj>sayName)(); // window
解决这问题方法有:
- this赋值给变量
- 函数间接调用(apply、call),或者bind
-
内存泄漏问题,是IE问题
function box() {
var oDiv = document.getElementById(‘oDiv’);
var text = oDiv.innerHTML;
oDiv.onclick = function() {
alert(text);
};
oDiv = null; //解除引用
}
PS:如果并没有使用解除引用,那么需要等到浏览器关闭才得以释放。
- 私有变量
有权访问私有变量和私有函数的公有方法称为特权方法(privileged method)。
一般是在构造函数,立即调用表达函数的对象中使用
var application = function() {
//私有变量和函数
var components = new Array();
//初始化
components.push(new BaseComponent());
//创建 application 的一个局部副本
var app = new BaseComponent();
//公共接口
app.getComponentCount = function() {
return components.length;
};
app.registerComponent = function(component) {
if (typeof component == "object") {
components.push(component);
}
};
//返回这个副本
return app;
}();
匿名函数、返回函数、闭包这三个常常结合在一起使用。
var say = (function(){
var name = 'lc';
return function(){
console.log(name);
}
})();
闭包总结:
函数闭包主要也常用两个用途:
- 封装变量(块级作用域,,这个作用主要解决js自身没块级作用问题)
- 不管你是私有属性和私有方法,还是私有变量,全都是延续局部变量的寿命。
八、函数作为参数
函数作为参数的时候,如果函数是命名函数,当做参数的时候只需要传参数名,不带括号。