深入理解JavaScript函式

JavaScript中的函式
1. 函式的定义
两种定义形式:通过函式定义表示式来定义通过函式宣告语句来定义函式宣告语句定义一个函式
//计算阶乘的递回函式
function factorial(x){
if (x
return x*factotial(x-1);
}
函式定义表示式定义一个函式
var s= function sum(x,y){
return x+y;
}
tips:以表示式方式定义的函式(特别适合用来定义那些只会用到一次的函式),函式名是可选的,
也就可以直接写成这样
var s= function(x,y){
return x+y;
}
2.函式命名
函式的名称通常是动词或者以动词为字首的词组 如:funciton saveMessage(){}函式名的第一个字元通常为小写当我们命名的函式名比较长时,一种是驼峰式命名 如:function readSystemFile(){}另一种是以下划线分割单词,如:function make_products_iPhones(){}有一些函式是用作内部函式或者私有函式的,通常以一条下划线为字首。 如: _login()3.函式的呼叫
普通的函式呼叫example:
printprops({x:1})
var total = distance(0,1,2)+ distance(3,4,5)
var probability = factorial(5)/factoral(10)
物件方法的呼叫概念:如果函式表示式是一个属性访问表示式(即该函式是一个物件的属性或者阵列的一个函式),
那么该呼叫表示式就是一个方法呼叫表用表示式。
example:
//定义一个物件
var person = {
name: lihua ,
age: 18 ,
sex: 女 ,
send: function(){//返回 person的name、age、sex
this.message = this.name + this.age + this.age;
}
};
person.send();//这条语句就是函式的方法呼叫
person.message; //得到name、age、sex相关资讯
tips:
1.函式返回的值倘若是因为直译器到达结尾,返回值就是undefined;倘若函式返回是因为直译器执行到一条return语句,
则返回return语句后面的值;如果return语句后面没有值则返回undefined。
2.在ECMAScript3和在非严格模式下的ECMAScript5的函式呼叫规定中,this的值(呼叫上下文)的值是全域性物件,但是在
严格模式下this的值却是undefined,因此可也用this来判断当前环境是否为严格模式,此外ECMAScript2015 (简称es6)严格模式请参考以下地址。
ECMAScipt的第六个版本 https://developer.mozilla.org/zh-CN/
3.方法呼叫和普通的函式呼叫的一个重要区别就是:呼叫上下文(即this的值),通常this关键字指向成为呼叫上下文的物件,
属性访问表示式:物件名.方法名(通常使用.运算子访问属性)或者"[]"进行属性访问操作。(this关键字的相关内容是十分重要的)
建构函式的呼叫建构函式就是用来初始化先建立的物件,通常使用关键字new来呼叫建构函式,当使用new关键字来呼叫建构函式的时候就会自动
建立一个新的的空物件,而建构函式只需要初始化这个新物件的状态(属性和方法),呼叫建构函式的话,新的物件的原型(prototype)等于
建构函式的原型(prototype)属性,由此引出一个特性:通过同一个建构函式建立的所有物件都继承同一个相同的物件。
凡是没有形参的建构函式都可以省略圆括号,以下两行程式码是等价的。
var fn = new Object();
var fn = new Object;
tips:
1.建构函式通常不使用return关键字,进行初始化新物件,执行完函式体,就呼叫建构函式表示式的计算结果作为新物件的值,显示返回。
2.倘若建构函式使用return语句返回一个物件,那么呼叫表示式的值就是这个物件,而不是this指向的物件。
3.建构函式里没有显式呼叫return时,预设是返回this物件,也就是新建立的例项物件。
4.return的是五种简单资料型别:String,Number,Boolean,Null,Undefined。这种情况下,忽视return值,依然返回this物件
间接呼叫JavaScript中的函式也是物件,所以函式物件也可以包含方法。函式的间接呼叫用到的call()和apply()方法。这两个方法都能显示指定呼叫所需的this的值,这就引出一个特性:任何函式都可以作为物件的方法来呼叫,哪怕这个函式不是那个物件的方法。(这也就是在实际开发中我们也会常用这两个方法的原因之一)1.call()方法呼叫一个函式, 其具有一个指定的this值和分别地提供的引数(引数的列表) this的值并不一定就是该函式执行真正的this值,在非严格模式下,倘若this的值指向null和undefined的话
会自动指向全域性物件(浏览器中就是window物件),同时值为原始值(数字,字串,布林值)的this会指向该原始值的自动包装物件。
2.apply()方法呼叫一个具有给定this值的函式,以及作为一个阵列(或类似阵列物件)提供的引数。
为this指定一个物件,使用apply时只需要写一次这个方法然后再另一个物件中继承它,继而不用在这个新的物件重新来写它。
tips:call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是引数列表,而apply()方法接受的是一个引数阵列。
4.函式中的形参与实参
可选的形参可变长的实参列表:实参物件 arguments 它是一个类阵列物件,通过数字下表就能够访问传入函式的实参值,它包含length属性,让函式可以操作任意数量的实参。callee和caller属性callee属性指代当前正在执行的函式,caller指代呼叫当前正在执行的函式的函式,它可以访问栈。而callee可以来递回呼叫自身。5.函式的闭包(!important)
概念:通俗地讲函式的闭包就是在一个函式内部定义另一个函式,而这个内部的函式(子函式)可以呼叫包裹它的函式(父函式等、"爷爷、太爷爷...")的变数。也可以认为闭包就是能够读取其他函式内部变数的函式。变数作用域:全域性变数:任何函式内部都可以访问全域性作用域区域性变数:在函式外部无法读取变数tips
javascript是没有像Java、C++那样用一对“{}”括起来的块级作用域,但是在es6中可以使用let关键字实现块级作用域。
词法作用域:变数的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源代码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。with和eval除外,所以只能说JS的作用域机制非常接近词法作用域(Lexical scope)。var scope = "global scope";
function checkScope(){
var scope = "local scope";
function f(){return scope;}
return f();
}
checkScope(); //输出可以得到 local scope
闭包的用途i. 读取函式内部的变数、函式巢状函式ii. 让这些变数的值始终保持在内存中(全域性变数的值不会在被函式呼叫过后自动清除,由GC回收)iii. 避免全域性变数的污染、让私有成员存在闭包的注意事项i. 由于闭包会使得函式中的变数都被储存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的效能问题,在IE中可能导致内存泄露。解决方法是,在退出函式之前,将不使用的区域性变数全部删除。ii. 闭包会在父函式外部,改变父函式内部变数的值。所以,如果你把父函式当作物件(object)使用,把闭包当作它的公用方法(Public Method),把内部变数当作它的私有属性(private value),这时一定要小心,不要随便改变父函式内部变数的值。以下例子来检验自己是否已经掌握了闭包的执行机制。example 0ne: var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()());
example Two:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()());
6.函式的属性、方法以及建构函式
length属性argument.length属性表示传入函式的实参的个数,而函式本身的length属性则有不同含义,它表示函式定义时的实际形参个数。prototype(原型)属性这个属性指向物件的引用,而这个物件就被称为原型物件(prototype object),每个函式都包含这个属性,都包含不同的原型物件;在JavaScript中每个函式都有一个特殊的属性叫作原型(prototype)function doSomething(){}
console.log(doSomething.prototype);
-----------------------------------
结果: 这就是 原型物件
{
constructor: ƒ doSomething(), //建构函式
/* 这些又是这个建构函式里面的方法或者属性
arguments: null
caller: null
length: 0
name: "doSomething"
prototype: {constructor: ƒ} 建构函式的原型属性
__proto__: ƒ ()*/
__proto__: { //原型属性
constructor: ƒ Object(),
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
-------------------------------
function doSomething(){}
doSomething.prototype.eat = "food" //doSomething函式的原型(prototype)属性物件新增eat的属性
var doSomeInstancing =new doSomething() //建立doSomething函式的例项 通过new关键字来呼叫该函式,它返回这个函式的例项化物件给doSomeInstancing
doSomeInstancing.prop = "add value"; //给物件doSomeInstancing新增一个“name为prop值为add value”的属性
console.log(doSomeInstancing); //输出这个doSomeIntancing物件 结果现实这个物件有两个属性,一个是:prop: "add value" 另一个是:__proto__: Object
console.log(doSomething.prototype); //输出doSomething.prototype的值是一个物件{eat: "food",constructor: ƒ doSomething(), __proto__: Object
显然doSomeInstancing.__proto__属性与doSomething.prototype(建构函式的原型属性的值是相同)它们的值是相等的。(可以通过===来判断 __ptototype__为隐式原型)
call()、apply()和bind()方法 在前面的函式的间接呼叫中已经介绍了call()以及apply(),在这里就不再叙说,就详细介绍ECMAScript5新增的bind()方法bind()方法中的bind翻译过来就是捆绑、系结之意,作用就是将函式系结至某个物件中,倘若一个函式呼叫了bind()方法并传入一个物件作为引数,那么这个方法将返回新的函式。
function f(y){return this.x+y;} //待系结的函式
var o = {x :1};//将要被系结的物件
var g = f.bind(o); //将函式f系结至物件o 相当于var o={x:1, f:function f(y){return this.x+y;}}
console.log(g(2)); //通过g(y)呼叫o.f(y) 输出3
bind()方法除了除了第一个实参之外,传入bind()的实参也会系结至this.这种应用是一种常见的函数语言程式设计技术,被称为柯里化(currying)
var sum = function(x,y){return x+y;}
var succ = sum.bind(null,1);
succ(2)
toString()方法该方法返回字串Function()建构函式高阶函式记忆(mem
最后
以下是总结出来最全前端框架视讯,包含: javascript/vue/react/angualrde/express/koa/webpack 等学习资料。