JS Function函数深入解析Word文档下载推荐.docx
- 文档编号:19967760
- 上传时间:2023-01-13
- 格式:DOCX
- 页数:30
- 大小:82.20KB
JS Function函数深入解析Word文档下载推荐.docx
《JS Function函数深入解析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《JS Function函数深入解析Word文档下载推荐.docx(30页珍藏版)》请在冰豆网上搜索。
3.};
定义了一个变量
sum
并将其初始化为一个函数,注意到
function
关键字后面并没有函数名,这是因为在使用函数表达式定义函数,没必要使用函数名,通过变量
即可引用函数。
还要注意函数末尾有个分号,就像声明其他变量一样。
new
构造函数,虽然这种用法也是函数表达式,但该用法不推荐。
因为这种语法会导致解析两次代码(第一次是解析常规的ECMAScript代码,第二次是解析传入构造函数中的字符串),影响性能。
使用
构造函数,构造函数可以接受任意数量的参数,但最后一个参数始终都被看成是函数体,前面的参数则枚举出了新函数的参数。
1.varsum=newFunction('
num1'
'
num2'
returnnum1+num2;
'
);
2.sum;
//
3.functionanonymous(num1,num2
4./**/){
5.returnnum1+num2;
6.}
当使用不带圆括号的函数名是访问函数指针,而非调用函数。
理解参数
ECMAScript中所有参数传递的都是值(即使是引用也是传递的地址值,不是引用传递参数(可参考JavaScript传递参数是按值传递还是按引用传递))。
ECMAScript函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型。
之所以这样,是因为ECMAScript中的参数在内部是用一个数组表示的。
函数接收到的始终都是这个数组,而不关心数组中包含哪些参数。
在函数体内,可以通过
arguments
对象来访问这个数组。
从而获取传递给函数的每个参数。
1.functionfunc(){
2.console.log(Object.prototype.toString.call(arguments));
4.
5.func();
//[objectArguments]
1.关于
的行为,它的值永远与对应命名参数的值保持同步。
因为
对象中的值会自动反映到对应的命名参数。
所以修改
arguments[1]
,也就修改了
num2
。
不过这并不是说读取这两个值会访问相同的内存空间,它们的内存空间是独立的,但他们值会同步(WHY?
?
),要是JavaScript能直接访问内存就好了验证一下。
2.但如果只传入了一个参数,那么
设置的值不会反映到命名参数中,这是因为
对象的长度是由传入参数个数决定的,不是由定义函数时的命名参数个数决定的,没有传递值的命名参数将自动被赋予
值,这就跟定义了变量但没初始化一样。
1.functiondoAdd(num1,num2){
2.console.log(arguments.length);
3.console.log(num2)
4.arguments[1]=10;
5.console.log(num2);
7.doAdd(5,0);
//2010
9.doAdd(5);
//1undefiendundefined
没有重载
ECMAScript函数不能像传统意义上那样实现重载,而在其他语言中(Java),可以为一个函数编写两个定义,只要这两个定义的签名(接收参数的类型和数量)不同即可。
不能实现重载的原因:
1.ECMAScript函数没有签名,因为其参数是由包含零个或多个值的数组来表示的。
没有函数签名,真正的重载是不可能做到的。
在ECMAScript中定义两个名字相同的的函数,则该名字只属于后定义的函数。
如何实现类似于Java中的重载呢,其实可以通过判断传入函数的参数类型和个数来做出不同响应。
1.functionreload(){
2.if(arguments.length==0){
3.console.log('
没传参'
4.}elseif(arguments.legth==1){
5.console.log('
传了一个参数'
6.}
7.}
1.深入理解:
将函数名想象为指针,也有助于理解为什么ECMAScript中没有函数重载的概念。
1.functionadd(){
2.return100;
4.functionadd(num){
5.returnnum+200;
7.
8.//实际上和下面代码没什么区别
9.functionadd(){
10.return100;
11.}
12.add=function(num){
13.returnnum+200;
函数声明和函数表达式
实际上解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁。
JavaScript运行机制浅探
中了解到对于解释型语言来说,编译步骤为:
1.词法分析(将字符流转换为记号流,是一对一的硬性翻译得到的是一堆难理解的记号流)
2.语法分析(这里进行所谓的变量提升操作,其实我觉得是把这些提升的变量保存在语法树中。
要构造语法树,若发现无法构造就会报语法错误,并结束整个代码块的解析)
3.之后可能有语义检查,代码优化等。
得到语法树后就开始解释执行了。
解释性语言没有编译成二进制代码而是从语法树开始执行。
解析器会先读取函数声明,并使其在执行任何代码之前可用。
至于函数表达式,则必须等到执行阶段才会被真正赋值。
什么意思呢?
虽然两者都进行了变量提升,待真正执行时构造活动对象从语法树种取声明添加到执行环境中,但一个是函数提升,一个是变量提升。
1.//函数声明
2.console.log(func);
//functionfunc(){}
3.functionfunc(){
6.
7.//函数表达式
1.console.log(func1);
2.varfunc1=function(){};
3.console.log(func1);
//function(){}
作为值的函数
因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。
不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。
1.functioncallSomeFunction(someFunction,someArgument){
2.returnsomeFunction(someArgument);
5.functionconcated(str){
6.return"
Hi"
+str;
9.callSomeFunction(concated,'
xx'
//'
Hixx'
从一个函数中返回另一个函数的应用:
假设有一个对象数组,想要根据某个对象属性对数组进行排序,但传给
sort()
方法的比较函数要接收两个参数,即要比较的值。
我们需要一种方式来指明按照哪个属性来排序。
我们可以定义一个函数它接收一个属性名,然后根据这个属性名来创建一个比较函数。
默认情况下,
sort
函数会调用每个对象的
toString()
方法以确定它们的次序。
1.functioncreateCompare(property){
2.returnfunction(obj1,obj2){
3.varvalue1=obj1[property],
4.value2=obj2[property];
5.if(value1<
value2)return-1;
6.elseif(value1>
value2)return1;
7.elsereturn0;
8.}
9.}
10.vardata=[{name:
aa'
age:
20},{name:
bb'
12},{name:
cc'
30}];
11.data.sort(createCompare("
age"
));
//[{name:
30}]
函数的内部属性
:
类数组对象,包含传入函数中所有参数。
是每个函数自身的属性,之所以可以直接访问
,是因为命名空间?
以下变化是为了加强JavaScript语言的安全性,这样第三方代码就不能在相同的环境下窥视其他代码了。
∙
callee
属性:
是一个指针,指向拥有
对象的函数。
严格模式访问会导致错误。
1.//一般阶乘函数
2.functionfactorial(num){
3.if(num<
=1){return1;
}
4.else{
5.returnnum*factorial(num-1);
定义阶乘函数用到递归算法,这样定义是没问题。
缺点:
这个函数的执行与函数名
factorial
紧紧耦合在一起。
万一出现改变函数指向的这种情况就不太好了,
1.factorial=function(){}
2.factorial(3);
//undefiend
为了消除这种现象。
1.functionfactorial(num){
2.if(num<
}
3.else{
4.returnnum*arguments.callee(num-1);
5.}
∙这样无论引用函数使用的是什么名字都可以保证完成递归。
caller
不过在非严格模式下这个属性始终是
即使在严格模式下访问也会出错。
增加这个属性是为了分清
arguments.caller
和函数对象上的
属性。
1.functiona(){
2.returnObject.getOwnPropertyNames(arguments);
4.a();
//["
length"
"
callee"
]
this
行为与Java/C#中的
大致类似。
引用的是函数据以执行环境对象(当在网页的全局作用域中调用函数时,
对象引用的就是
window
)。
不止是ECMAScript5中新增函数对象上的属性,还是
上的属性。
保存着调用当前函数的函数的引用。
如果是在全局作用域中调用当前函数,它的值为
null
Object.getOwnPropertyNames(Function);
//
["
name"
arguments"
caller"
prototype"
1.functionouter(){
2.inner();
4.functioninner(){
5.console.log(inner.caller);
//为了实现更松散的耦合,arguments.callee.caller
8.outer();
//functionouter(){inner()}
严格模式下不能为函数的
属性赋值,否则会导致出错。
函数的属性和方法
∙length:
表示函数希望接收的命名参数的个数(也就是定义的形参的个数)。
1.functionsayName(name){
2.//
4.functionsum(num1,num2){
5.//
7.functionsayHi(){
8.//
10.
11.sayName.length;
//1
12.sum.length;
//2
13.sayHi.length;
//0
∙prototype:
对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。
诸如toString和valueOf等方法实际上都保存在Object.prototype名下(原生构造函数比如Function,Array等
在自己原型上重写了toString)。
在ECMAScript5中,prototype属性是不可枚举的,因此使用for-in无法发现。
Object.getOwnPropertyDescriptor(Function,’prototype’);
//Object{writable:
false,enumerable:
false,configurable:
false}
∙每个函数上有两个可用的方法:
apply和call。
这两个方法实际上是在Function.prototype上,
Object.getOwnPropertyNames(Function.prototype);
apply"
bind"
call"
toString"
constructor"
]
它是在JavaScript引擎内部实现的。
因为是属于Function.prototype,所以每个Function的实例都可以用(自定义的函数也是Function的实例)。
都是在特定的作用域或自定义的上下文中调用执行函数,实际上等于设置函数体内
对象的值。
apply
参数一为在其中运行函数的作用域,参数二为参数数组(可以是数组,也可以是
对象)。
5.functioncallSum1(num1,num2){
6.returnsum.apply(this,arguments);
//sum.apply(this,[num1,num2])
9.callSum1(10,30);
//40
严格模式下,未指定环境对象而调用函数,
值不会转型为
除非明确把函数添加到某个对象或者调用
或
call
,否则
值将是
undefined
参数一没有变化,变化的是其余参数都是直接传递给函数,参数必须都列出来。
1.functioncallSum1(num1,num2){
2.retrunsum.call(this,num1,num2);
5.callSum1(10,30);
和
真正强大的地方是能够扩充函数赖以运行的作用域,改变函数的执行环境。
bind
ECMAScript5定义的方法,也是
Function.prototype
上的方法。
用于控制函数的执行上下文,返回一个新函数,这个函数的
值会被绑定到传给
bind()
函数中的值。
1.window.color="
red"
;
2.varo={color:
blue'
};
3.functionsayColor(){
4.console.log(this.color);
7.varnewobj=sayColor.bind(o);
8.newobj;
//functionsayColor(){
9.console.log(this.color);
10.}
11.newobj==sayColor;
//false
12.newobj();
//blue
深入理解:
可以将函数绑定到指定环境的函数。
接收一个函数和一个环境,返回在给定环境中调用给定函数的函数。
1.functionbind(func,context){
2.returnfunction(){
3.func.apply(context,arguments);
//这里创建了一个闭包,arguments使用的返回的函数的,而不是bind的
4.}
当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。
1.functionbind(func,context,args){
3.func.call(context,args);
4.};
4.
toString,toLocaleString
返回函数代码的字符串形式,返回格式因浏览器而异,有的返回源码,有的返回函数代码的内部表示,由于存在差异,用这个也实现不了什么功能。
5.
valueOf
返回函数的自身引用。
变量,作用域,内存问题
JavaScript接近词法作用域,变量的作用域是在定义时决定而不是在执行时决定,也就是说词法作用域取决于源码。
JavaScript引擎在执行每个函数实例时,都会为其创建一个执行环境,执行环境中包含一个AO变量对象,用来保存内部变量表,内嵌函数表,父级引用列表等语法分析结构(变量提升在语法分析阶段就已经得到了,并保存在语法树中,函数实例执行时会将这些信息复制到AO上)。
ECMA-262定义,JavaScript松散类型的本质决定了它只在特定时间用于保存特定值的一个名字而已,由于不存在定义某个变量必须要保存何种数据类型值得规则,变量的值及其数据类型可在脚本的生命周期内改变。
∙基本类型和引用类型的值:
ECMAScript变量可能包含两种不同数据类型的值:
基本类型值,引用类型值。
1.基本类型值:
简单的数据段。
2.引用类型值:
那些可能由多个值构成的对象。
是保存在内存中的对象,JavaScript不允许直接访问内存中的位置,也就说不能直接操作对象的内存空间。
在操作对象时实际上是在操作对象的引用而不是实际的对象。
为此,引用类型值是按引用访问的。
(这种说法不严密,当复制保存着对象的某个变量时,操作的是对象的引用。
但在为对象添加属性时,操作的是实际的对象)
在将一个值赋给变量时,解析器必须确定这个值是基本类型值还是引用类型值。
5种基本数据类型:
Undefined,Null,Boolean,Number,String
(很多语言中字符串以对象形式来表示因此被认为是引用类型,但ECMAScript放弃这一传统)。
这5种基本类型是按值访问的,因此可以操作保存在变量中的实际的值。
∙动态的属性
∙复制变量的值:
在从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。
如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。
当从一个变量向另一个变量赋值引用类型值值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中,不同的是,这个值的副本实际上是个指针(可以理解为复制了地址值),而这个指针指向存储在堆中一个对象。
复制操作结束后两个变量实际上将引用同一个对象。
∙传递参数:
ECMAScript中所有函数的参数都是按值传递的,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。
基本类型值得传递如同基本类型变量的复制一样,引用类型值的传递如同引用类型变量的复制一样。
很多人错误认为:
在局部作用域中修改的对象会在全局作用域中反映出来这就说明是按引用传递的。
为了证明对象是按值传递的,
1.functionsetName(obj){
2.obj.name="
xx"
3.obj=newObject();
4.obj.name="
bb"
7.varp=newObject();
8.setName(p);
9.p.name;
//"
如果是按引用传递的,即传递的不是地址值而是堆内存中整个p对象,在
setName
中为其添加了一个新名字叫
obj
,又给其添加
name
属性后,将这个
内容重新填充为新对象,那么之前的那个对象就不存在了更别说有
”xx”
的名字属性,但是
p.name
仍然访问到了。
这说明即使在函数内部修改了参数值,但原始的引用仍然保持未变。
实际上,当在函数内部重写
时,这个变量引用的就是一个局部对象了,而这个局部对象会在函数执行完毕后被立即销毁。
∙类型检测:
检测一个变量是不是基本数据类型用
typeof
是最佳工具,但如果变量的值是除了函数的对象或
[];
”object”
null;
,变量值为函数时
typeoffunction(){};
”function”
(ECMA-262规定任何在内部实现
[[call]]
方法的对象都应该在应用
操作符返回
但在检测引用类型值时,这个操作符用处不大,因为我们并不是想知道它是个对象,而是想知道它是某种类型对象。
如果变量是给定引用类型的实例,
instanceof
操作符会返回
true
所有引用类型值都是
Object
的实例。
如果使用
操作符检测基本类型的值,则该操作符始终会返回
,因为基本类型不是对象。
执行环境及作用域
∙执行环境(executioncontext):
也称为作用域,定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
全局执行环境是最外
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- JS Function函数深入解析 Function 函数 深入 解析
![提示](https://static.bdocx.com/images/bang_tan.gif)