函数
函数就是用来执行任务的代码块,函数需要通过调用的方式来完成代码的执行,函数的最大好处:包含在函数内容的代码块,可以重复执行。函数的作用可以理解成:将完成任务的代码块"封装”起来,供其他调用方无限制的使用
一、函数的定义
1. 声明式(函数声明):
- 函数声明的关键字 : function
- function 关键字 和 var 关键字的行为几乎一致,都会在内存上开辟一块空间,用来存储变量之中的数据;
- 声明式 : 规定 : 不要漏掉函数的名字!
- 函数的命名规则和变量命名规则完全一致;
- 函数推荐驼峰命名;
- 变量推荐_命名;
function 函数名( 条件 ){
//函数体
}
2. 赋值式(函数表达式)
- 赋值式函数声明可以没有函数名!
- 函数定义和调用规范 : 先声明再调用!;
- 命名函数表达式
var test = function abc(){
console.log("hello world");
//abc 是无法被调用 , 因为他的作用范围是局部的;
}
- 匿名函数表达式–
var foo = function(){
console.log("hello world");
}
二、函数的参数、return
- 形式参数:形参个数不限
- 实际参数
- arguments:实参参数都使用 arguments 关键字进行接收, 使用 [] 进行取出;
function sum(a,b){ //a,b为形参
var c = a + b;
console.log(c);
}
sum(1,2); //1,2为实参
- 计算隐藏的实参的个数 用 arguments.length。
function test(a,b,c,d){
for(var i = 0;i < arguments.length;i++){
document.write(arguments[i]);
}
}
test(11,2,3);
- 计算形参个数 函数名.lenght
function sum(a,b,c,d){
console.log(sum.length);
}
sum(11,2,3);
- 实参到形参有映射
function sum(a,b){
//实参到形参有映射
a = 2;
arguments [0] = 3;
console.log(a); //----3
console.log(arguments[1]); // undefined
//无映射 b为变量
b = 2;
console.log(arguments[1]); // ----undefined
console.log(a); // 3
console.log(b); //2
return ;
}
sum(1);
8. 函数返回值return
return 的特性
- 在函数之中,只要遇到了return关键字,表示函数执行结束,会立即跳出函数执行。
- 无论有多少个return ,只会执行第一个return,因为后面的就不执行了。
- 因此函数有一个特点, 返回值只能有一个;
- 函数的运算结果可以是什么样的数据类型;
- 任意数据类型都可以返回;
- 如果函数返回了一个函数会发生什么 ? (产生闭包:具体详细在后续讲到)
function foo(){
console.log("foo");
function poo(){
console.log("poo")
}
return poo;
}
var res = foo();
// 此时 res 里面存储的是 poo 函数的地址;
// 此时的res 和 poo 完全一样;
// res();
三、立即执行函数
注意:只有表达式才能被执行符号()执行
立即执行函数定义:
- 此类函数没有声明,在一次执行过后释放。适合做初始化工作
- 针对初始化功能的函数
- 只有表达式才能被执行符号执行
(function (){}()); //w3c 标准
(function (){})();
还有一种方法就是把函数声明变成一个函数表达式(在function前加 +,-,!但是 *, / 不行)
+ function (){}();
!function (){}();
拓展:(立即执行函数的应用)
function test(){
console.log("a");
}();
//执行不了;这是一个声明,如果在函数下面换行再加一个test();就可以执行
例如:
function test(){
console.log("a");
}
test(); //可以执行。
//还有一种方法就是把函数声明变成一个函数表达式(在function前加 +,-,!但是 *, / 不行)
+ function test(){
console.log("a");
}(); //可以执行
var test = function (){
console.log("a");
}()
//可以执行;因为这是一个表达式
//可以看成是var test 声明加一个表达式 = function (){}()
//还有一种方法是在外面加一个大括号()
(function test(){
console.log("a");
}())
// 外面的大括号相当于把里面变成了表达式,所以可以立即执行
//立即执行函数执行完后立即被销毁释放也就是函数的引用test不会被保存,第二次执行系统会报错
//所以test引用名可以不加变成:
(function (){
console.log("a");
}()) // w3c标准的写法
四、js 函数的调用
1、函数调用:this 指向 window,返回值由 return 决定
function f1() {
console.log(this);
}
f1(); //window
2.方法调用(函数作为方法调用):this指向方法的调用者 ,返回值由return决定
var obj = {
hello: function() {
return "hello, " + this.username;
},
username: "Hans Gruber"
};
obj.hello(); // "hello, Hans Gruber"
注:上述hello()直接调用的时候,this的指向就成了问题。在这种情况下,this往往被指向全局对象(GlobalObject),在浏览器上一般就是window对象。
在ES5标准中,如果使用了strict mode,那么this会被设置为undefined:
function hello() {
"use strict";
return "hello, " + this.username;
}
hello(); // error: cannot read property "username" of undefined
3.构造函数调用:
this指向:当前构造函数构建的对象
返回值:
- 1.没有返回值,返回this
- return了一个基本数据类型(布尔值,字符串,数字,null,undefined),返回this
- 返回了一个复杂数据类型(对象,数组等),返回该对象
- 隐式原理:
function Student(name, age, sex) {
//隐式:
//var this = {
// name : "";
// age : "";
// sex : "";
// grade : "";
// }
this.name = name;
this.age = age;
this.sex = sex;
this.grade = 2017;
//隐式
// return this;
}
var student = new Student('zhangsan',10,'male');
代码1:
function Person() {
this.age = 20;
this.name = "张三";
console.log(this);
}
//构造函数调用模式:
var p1 = new Person(); //打印出该实例 Person {age: 20, name: "张三"}
代码2;
function P2() {
this.age = 18;
return "abc";
}
var p2 = new P2();
console.log(p2); //P2 {age: 18}
代码3:
function P3() {
this.age = 10;
return {}; //捣乱则返回空值
}
var p3 = new P3();
console.log(p3); //Object {}
console.log(p3.age);//undefined
代码4:
function P4() {
this.age = 10;
return [1, 3, 5];
}
var p4 = new P4();
console.log(p4);//[1, 3, 5]
代码5:
function Person(name,height){
// var this = {} 隐式写出来
this.name = name;
this.height = height;
this.say = function(){
console.log(this.say);
}
return {};//捣乱则返回空值
// return that; //隐式写出来,可以不用new对象
}
var person = Person ('zhangsan',180);
var person1 = Person ('lisi',175);
4、上下文调用
call 和 apply 是方法, 是所有函数都具有的方法。 Function.prototype
只要函数调用call/apply 方法,那么该函数就会立即执行。
this指向:
- 传递一个null/undefined------------------->window
- 传递一个数字、字符串、布尔值------->对应的基本包装类型的对象
- 传递一个对象------------------------------->指向该对象
返回值 :由return语句决定
对象的方法可以任意指派,而对象本身一直都是没有这方法的,注意是指派,通俗点就是,方法是借给另一个对象的调用去完成任务,原理上是方法执行时上下文对象改变了.
function f1(){
console.log(this);
}
//上下文模式
f1.call(null); //window
f1.call(undefined); //window
f1.call(1); //Number的实例
f1.call("abc"); //String的实例
五、函数的作用域[[scope]]
作用域
[[scope]]:每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。[[scope]]:每个javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域,其中存储了运行期上下文的集合。
作用域链:
[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
运行期上下文:
当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,执行上下文被销毁
查找变量:从作用域链的顶端依次向下查找
function a() {
function b() {
var b = 234;
}
var a = 123;
return b;
}
var glob = 100;
a();
a函数被定义时:
六、闭包
当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露
作用:
- 实现公有变量
eg:函数累加器 - 可以做缓存
eg:eater - 可以实现封装,属性私有化。
eg: Person();
function a() {
function b() {
var bbb = 234;
console.log(aaa);
}
var aaa = 123;
return b;
}
var glob = 100;
var demo = a();
demo();
七、函数的预编译
预解析:
查你的代码有没有语法错误; 如果有语法错误,直接终止程序;如果你的代码之中有语法错误,那么你的所有代码都不会执行!;
// 函数声明整体提升
// 变量 声明提升
- imply globla
暗示全局变量:即任何变量,如果未经声明就赋值,此变量就为全局变量(window)所拥有。(window就是全局变量的域) a = 10;
window.a = 10 - 一切声明的全局变量,全是window的属性 var a = 123 ---->相当于 window.a=123
预编译四部曲:(预编译发生在函数执行前一刻)
1、创建AO对象 (Activation Object 执行期上下文)
2、找形参和变量声明,将变量和形参名作为AO属性名,职位undefined。
3、将实参和形参的值统一。
4、在函数体里面找函数声明,值赋予函数体。
function fn(a){
console.log(a); // function a (){}
var a = 123;
console.log(a); //123
function a (){}
console.log(a); //123
var b = function (){}
console.log(b); // function (){}
function d(){}
}
fn(1);
// AO{
// a: 123
// b:functon (){}
// d;function d(){}
// }
function test(a,b){
console.log(a); // function (){}
console.log(b); // undefined
var b = 234;
console.log(b); // 234
a = 123;
console.log(a); //123
function a (){}
var a;
b = 234;
var b = function (){} //表达式
console.log(a); //123
console.log(b); //function (){}
}
test(1);
// AO{
// a: 123
// b: function () {}
// }
5、未经声明的变量归GO所有
// function test(){
// var a = b = 123; b变量归GO所有为全局变量
// }
// test();
console.log(a);//undefined
a = 100;
function dome(e){ //1
function e(){} //e:funtion e(){}
console.log(a); //undefined(要找自身的AO的a,还未赋值)
arguments[0] = 2;
console.log(e); //2
if(a){ // a为undefined所以 if 未执行
var b = 123;
function c(){}
}
var c;
a = 10;
console.log(b);//undefined
f = 123;
console.log(c) //undfined //func..
console.log(a);//10
}
var a;
dome(1);
console.log(a); //100
console.log(f); //123