一.函数

           Javascript是一门基于对象的脚本语言,代码复用的单位是函数,但它的函数比结构化程序设计语言的函数功能更丰富。JavaScript语言中的函数是“一等公民”,它可以独立存在;而且JavaScript的函数完全可以作为一个类来使用(而且它还是该类唯一的构造器);与此同时,函数本身也是一个对象,函数本身是function

实例。

函数的最大作用是提供代码复用,将需要重复使用的代码块定义成函数,提供更好的代码复用。函数可以有返回值,可以没有返回值

1.定义函数的三种方式

a)定义命名函数,

        语法格式如下:

function functionName(param1,param1,...){

staments;
}

  b)定义匿名函数

语法格式如下:

function(parameter list){
staments
};

与命名函数的区别是没有函数名,函数后面有个分号。

当通过这种语法格式定义了函数之后,实际上就定义了一个函数对象(即function实例),接下来可以将这个对象赋给另外一个变量。例如下面代码:

<script type='text/javascript'>
var f = function(name)
{
document.writeln('匿名函数<br/>');
document.writeln('你好'+name);
}
f('yukey');
</script>

   使用匿名函数提供更好的可读性。

     c)使用function类匿名函数

      JavaScript提供了一个function类,该类也可以用于定义函数,Function类的构造器的参数个数可以不受限制,function可以接受一系列的字符串参数,其中

最后一个字符串参数是函数的执行体,执行体的各语句以分号(;)隔开,而前面的各字符串参数则是函数的参数,看下面定义函数的方式:

<script type='text/javascript'>
var f = new Function('name',"document.writeln('Function定义的函数<br/>');"
+"document.writeln('你好'+name);"); f('yukey');
</script>

2.局部函数

局部函数在函数里定义,看下面代码

 <script type="text/javascript">
function outer(){
function inner1(){
document.write("局部函数11111<br/>");
} function inner2(){
document.write("局部函数22222<br/>");
}
inner1();
inner2();
document.write("结束测试局部函数<br/>");
}
outer();
document.write("调用outer之后...");
</script>

在外部函数里调用局部函数并不能让局部函数获得执行的机会。只有当外部函数被调用时,外部函数里调用的局部函数才获得执行的机会。

3.数,方法,对象,变量和类   函数是JavaScript的“一等公民”,函数是JavaScript变成里非常重要的一个概念,当使用JavaScript定义了一个函数之后,实际上可以得到如下四项。

函数:就像Java的方法一样,这个函数可以被调。

对象:定义一个函数时,系统也会创建一个对象,该对象时Function类的实例

方法:定义一个函数时,该函数通常会附加给某个对象,作为该对象的方法

变量:在定义一个函数的同时,也会得到一个变量

:在定义函数的同时,也得到一个与函数同名的类

定义函数之后,有如下两种方式调用函数

直接调用函数:直接调用函数总是返回该函数体内最后一条return语句的返回值;如果该函数体内不包含return语句,则直接调用函数没有返回值。

使用new关键字直接调用函数:通过这种方式调用总有返回值,返回值就是一个Javascript对象。

<script type="text/javascript">
var test = function(name)
{
return "你好,"+name;
}
var rval = test('Sherman');
var obj = new test("Sherman");
alert( rval+"\n"+obj);
</script>

可以看出,第一种方式直接调用函数,返回的是return语句返回值,第二种使用new关键字调用给函数,也就是将函数当成类来使用,得到的是一个对象。

下面程序定义了一个person函数,也就定义了一个person类,该person函数也会作为Person类唯一的一个构造器,定义了person函数时希望为该函数定义

了一个方法。

<script type="text/javascript">
function Person(name ,age){
this.name = name;
this.age = age;
this.info=function(){
document.writeln("我的名字是"+this.name+'<br/>');
document.writeln("我的年纪是"+this.age+'<br/>');
}
}
var p = new Person('Sherman',24);
p.info();
</script>

被this关键字修饰的的变量不再是局部变量,它是该函数的实例属性。

JavaScript定义的函数可以“附加”到某个对象上,作为该对象的方法。实际上如果没有明确指定将函数“附加”到哪个对象上,该函数默认“附加”到window

对象上,作为window对象的方法。

例如如下代码:

<script type="text/javascript">
function hello(name)
{
document.write(name+",您好<br/>")
}
window.hello("孙大圣");
p = {
//定义一个函数,该函数属于p对象
walk:function(){
for(let i = 0 ; i < 2 ; i++){
document.write("慢慢地走...<br/>"); }
}
};
p.walk();
</script>

4.函数的实例属性和类属性

由于JavaScript函数不仅仅是一个函数,而且是一个类,该函数还是此类唯一的构造器,只要在调用函数时使用new关键字,就可返回一个object,这个object

不是函数的返回值,而是函数本身产生的对象。因此JavaScript中定义的变脸不仅有局部变量,还有实例属性和类属性两种。根据函数中声明变量的方式,

中的变量有三种

  a)局部变量:在函数中以var声明的变量

b)实例属性:在函数中以this前缀修饰的变量

c)类属性:在函数中以函数名前缀修饰的变量

局量只能在函数里访问的变量。实例属性和类属性是面向对象的概念:实例属性是属于单个对象的,因此必须通过对象来访问,类属性是属于整个类本身

(也就是函数)的,因此必须通过类来访问。

同一个类只占用一块内存,因此每个类属性只占用一块内存;同一个类每创建一个对象,系统将为该对象的实例属性分配一块内存。

 <script type="text/javascript">
function Person(national,age){
this.age = age;
Person.national = national;
var bb = 0;
}
var p1 = new Person('中国',29);
with(document){
writeln("创建第一个Person对象");
writeln("p1的age属性为:"+p1.age+"<br/>");
writeln("p1的national属性为:"+p1.national+"<br/>");
writeln("通过Person访问静态national属性为:"+Person.national+"<br/>");
writeln("p1的bb属性为"+p1.bb+"<br/><hr/>");
} var p2 = new Person('美国',32);
with(document){
writeln("创建两个Person对象中后<br/>");
writeln("p1的age属性为:"+p1.age+"<br/>");
writeln("p1的national属性为:"+p1.national+"<br/>"); writeln("p2的age属性为:"+p2.age+"<br/>");
writeln("p2的national属性为:"+p2.national+"<br/>");
writeln("通过Person访问静态national属性为:"+Person.national+"<br/>"); }
</script>

浏览器输出:

创建第一个Person对象 p1的age属性为:29
p1的national属性为:undefined
通过Person访问静态national属性为:中国
p1的bb属性为undefined


创建两个Person对象中后
p1的age属性为:29
p1的national属性为:undefined
p2的age属性为:32
p2的national属性为:undefined
通过Person访问静态national属性为:美国

   值得指出的是,JavaSript和java不一样,它是一种动态语言,它允许随时为对象增加属性和方法,当直接为对象的某个属性赋值时,即可视为给对象增加属性

5.调用函数的3种方式

 5.1 直接调用函数

     如下代码:

//调用window对象的alert方法
window.alert();
//调用p对象的walk方法
p.walk();

当程序使用window对象调用方法时,window调用者可以省略

5.2 以call方式调用函数

直接调用函数的方式简单易用,但这种调用方式不够灵活,有时候调用函数时需要动态的传入一个函数引用,此时为了动态地调用函数,就需要call方法。call调用函数

语法格式为

函数引用.call(调用者,参数1,参数2,...)

下面通过call方法调用each函数:

<script type="text/javascript">
var each = function(array,fn){
for(var index in array){
fn.call(null,index,array[index]);
}
}
each([4,20,3],function(index,ele){
document.writeln("第"+index+"个元素是:"+ele+"<br/>");
});
</script>

浏览器输出:

第0个元素是:4
第1个元素是:20
第2个元素是:3

5.3 以apply()方法调用函数

apply()方法和call()方法比较类似,都可以动态的调用函数,他们的区别是:

a)通过call()方法调用函数时,必须在括号中列出每个参数

b)通过apply()动态地调用函数时,需要以数组形式一次性传入所有调用函数

以下代码示范了call()和apply()的关系

<script type="text/javascript">
var myfun = function(a,b){
alert('a的值是'+a+'\nb的值是'+b);
}
//以call()方式动态的调用函数
myfun.call(window,5,20);
//以apply()方式动态的调用函数
myfun.apply(window,[3,12]);
var example = function(num1,num2){
//直接用arguments代表调用example函数时传入的所有函数
myfun.apply(this,arguments);
}
example(20,40);
</script>

由此可见,apply()和call()对应关系如下:

函数引用.call(调用者,参数1,参数2,...); = 函数引用.apply(调用者,[参数1,参数2,...]);

6.函数独立性

虽然定义函数时可以将函数定义成某个类的方法,或定义成某个对象的方法。但JavaScript的函数是“一等公民”,他永远是独立的,函数永远不会从属于其他类,对象。

下面代码示范了函数的独立性:

<script type="text/javascript">
function Person(name){
this.name = name;
this.info = function (){
alert("我的name是:"+this.name);
}
} var p = new Person("Sherman");
//调用p对象的info方法
p.info(); var name = "测试名称";
//以window对象作为调用者来调用p对象的info方法
p.info.call(window);
</script>

当使用匿名内嵌函数定义某个类的方法是时,该内嵌函数一样是独立存在的,该函数也不是作为该类实例的附庸存在,这些内嵌函数也可以被分离出来独立使用,成为另一个对象的函数。如下代码再次证明函数的独立性:

<script type="text/javascript">
function Dog(name,age,bark)
{
this.name = name;
this.age = age;
this.bark = bark;
//使用内嵌函数为Dog实例定义方法
this.info = function(){
return this.name+"的年龄为:"+this.age+",它的叫声为:"+this.bark;
}
} var dog = new Dog("旺财",3,"汪汪,汪汪...");
function Cat(name,age){
this.name = name;
this.age = age;
}
//将dog实例的info方法分离出来,在通过call方法调用info方法
//此时cat为调用者
var cat = new Cat("Kitty",2)
alert(dog.info.call(cat));
</script>

7.函数提升

JavaScript允许先调用函数,然后再在后面定义函数,这就是典型的函数提升:JavaScript会将全局函数提升到根元素<script.../>元素的顶部定义

例如如下代码:

 <script type="text/javascript">
console.log(add(2,5));
function add(a,b){
console.log("执行add函数")
return a+b;
}
</script>

和下面代码效果是一样的:

 <script type="text/javascript">
function add(a,b){
console.log("执行add函数")
return a+b;
}
console.log(add(2,5));
</script>

效果如图:

如果使用程序先定义匿名函数,然后将匿名函数赋值给变量,在这种方式下依然会发生函数提升,但此时只提升被赋值的变量,函数定义本省不提升。例如

 <script type="text/javascript">
console.log(add(2,5)); var add = function(){
console.log("执行add函数");
return a+b;
}
</script>

效果如图:

局部函数会被提升到所在函数的顶部,如

 <script type="text/javascript">
function test() {
function add(a, b) {
console.log("执行add函数")
return a + b;
} console.log(add(2, 5));
}
test();
</script>

JavaScript编程时应尽量避免变量名和函数名同名。否则会发生覆盖的情形:分两种情况

a)定义变量时只用var定义变量,不分配初始值,此时函数的优先值更高,函数会覆盖变量。

b)定义变量时为变量值指定了初始值,此时变量的优先值更高,变量会覆盖函数

测试代码如下:

<script type="text/javascript">
function a(){}
var a;
console.log(a);
var b;
function b(){}
console.log(b);
var c = 1;
function c(){};
console.log(c); function d(){}
var d = 1;
console.log(d);
</script>

效果如下:

8.箭头函数

箭头函数相当于其他语言的Lambda表达式或闭包语法,箭头函数是普通函数的简化写法。语法格式如下:

(param1,param2,param3,...) => {staments}

相当于定义了如下函数:

function(param1,param2,param3,...){}

如果箭头函数的执行体只有一条return语句,则允许省略函数执行体的花括号和return关键字。

如果箭头函数的形参只有一个参数,则允许省略形参列表的圆括号。

如果箭头函数没有形参,则圆括号不可以省略。

(param1,param2,param3,...) => expression
//等同于(param1,param2,param3,...) =>{return expression}
singleParam => {staments}
//等同于(singleParam) => {staments}

下面代码示范了箭头函数代替传统函数:

<script type="text/javascript">
var arr = ["yuekey","fkit","leegang","sczit"];
var newArr1 = arr.map(function(ele){
return ele.length;
});
var newArr2 = arr.map((ele) =>{return ele.length});
var newArr3 = arr.map(ele => ele.length);
console.log(newArr3);
arr.forEach(function(ele){
console.log(ele);
});
arr.forEach((ele) => {console.log(ele);})
arr.forEach(ele => console.log(ele)); </script>

与普通函数不同的是,箭头函数并不拥有自己的this关键字,对于普通函数而言,如果程序通过new调用函数创建对象,那么该函数中的this代表所创建的对象;

如果直接调用普通函数,那么该函数的this代表全局对象(window)。例如,如下代码示范了this关键字的功能。

<script type="text/javascript">
function Person(){
this.age = 0;//Person()作为构造器使用时,this代表构造器创建的对象
setInterval(function growUp(){
console.log (this=== window);//对于普通函数来说,this代表全局对象window,总是返回true
this.age++;
},1000);
}
var p = new Person();
setInterval(function(){
console.log(p.age);//此处访问p对象的age,总是0
},1000);
</script>

箭头函数中的this总是代表包含箭头函数的上下文,例如:

<script type="text/javascript">
function Person(){
this.age = 0;
setInterval(() =>
{
console.log(this === window);
this.age++;//this总是代表包含箭头函数的上下文
},1000);
} var p = new Person();
setInterval(() => console.log(p.age),1000);//此处访问的是p对象的age,总是不断加1 </script>

如果在全局范围内定义箭头函数,那么箭头函数的上下文就是window本身,this代表全局对象window对象。

<script type="text/javascript">
var f = () => {return this;};
console.log(f() === window);//输出true </script>

箭头函数并不绑定arguments,因此不能在箭头函数中通过arguments来访问调用箭头函数的参数,箭头函数的arguments总是引用当前上下文的arguments。例如

<script type="text/javascript">
var arguments = "Sherman";
var arr = () => arguments;
console.log(arr()); function foo(){
var f = (i) => 'Hello,'+arguments[1];
return f(2);
}
console.log(foo("Sherman","Leegang"));//箭头函数中的arguments引用当前上下文的arguments,此时代表调用foo函数的参数
</script>

9.函数的参数处理

大部分时候,函数都需要接受参数传递。与Java完全类似,JavaScript的参数传递也全部采用值传递方式。

9.1基本类型和复合类型的参数传递

对于基本类型参数,JavaScript采用值传递方式,当通过实参调用函数时,传入函数里的并不是实参本身,而是实参的副本,因此在函数中修改参数值并不会对实参

有任何影响:

 <script type="text/javascript">
function change(arg1) {
arg1 = 10;
document.writeln("函数执行中arg1的值为:" + arg1 + "<br/>");
}
var x = 5;
document.writeln("函数调用前x的值为"+x+"<br/>");
change(x);
document.writeln("函数调用之后的x值为"+x+"<br/>"); </script>

对于复合类型的参数,实际上采用的依然是值传递方式,只是很容易混淆。看如下程序

<script type="text/javascript">
function changeAge(person)
{
person.age = 10;
document.writeln("函数执行中age的值为:" + person.age + "<br/>");
person = null;
}
var person = {age:5};
document.writeln("函数调用之前age的值为"+person.age+"<br/>");
changeAge(person);
document.writeln("函数调用之后的age值为"+person.age+"<br/>");
document.writeln("person对象为"+person);
</script>

9.2空参数

在JavaScript中,在函数声明时包含了参数,但调用时没有传入实参,这种情况是允许的,JavaScript会自动将参数值设置为undefined值,对于JavaScript来说,函数名就是函数的唯一标识。

如果先后定义两个同名,形参列表不同的函数,这不是函数重载,这种情况后面定义的函数会覆盖前面的函数。

9.3参数类型

JavaScript是弱类型语言,参数列表无需声明参数类型。

“鸭子类型”的理论认为,弱类型语言的函数需要接收参数时,则应先判断参数类型,判断参数是否包含了需要访问的属性、方法。当条件都满足时 ,程序才会真正

执行。看如下代码

<script type="text/javascript">
function changeAge(person) {
if (typeof person == 'object' && typeof person.age == 'number') {
document.writeln("函数调用之前age的值为" + person.age + "<br/>");
person.age = 10;
document.writeln("函数执行中age的值为:" + person.age + "<br/>");
}
else {
document.writeln("参数类型不符合" + typeof person + "<br/>")
}
}
changeAge();
changeAge("Sherman");
changeAge(true); p = {abc:34};//json格式创建第一个对象
changeAge(p); person = {age:25};//json格式创建第二个对象
changeAge(person);
</script>

Javascript二(函数详解)的更多相关文章

  1. [转]javascript console 函数详解 js开发调试的利器

    javascript console 函数详解 js开发调试的利器   分步阅读 Console 是用于显示 JS和 DOM 对象信息的单独窗口.并且向 JS 中注入1个 console 对象,使用该 ...

  2. JavaScript valueOf() 函数详解

    valueOf()函数用于返回指定对象的原始值. 该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法. 所有主流浏 ...

  3. JavaScript toString() 函数详解

    toString()函数用于将当前对象以字符串的形式返回. 该方法属于Object对象,由于所有的对象都"继承"了Object的对象实例,因此几乎所有的实例对象都可以使用该方法. ...

  4. JavaScript hasOwnProperty() 函数详解

    hasOwnProperty()函数用于指示一个对象自身(不包括原型链)是否具有指定名称的属性.如果有,返回true,否则返回false. 该方法属于Object对象,由于所有的对象都"继承 ...

  5. javascript console 函数详解 js开发调试的利器

    Console 是用于显示 JS和 DOM 对象信息的单独窗口.并且向 JS 中注入1个 console 对象,使用该对象 可以输出信息到 Console 窗口中. 使用 alert 不是一样可以显示 ...

  6. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  7. javascript 函数详解2 -- arguments

    今天我们接着上篇文章来继续javascript函数这个主题.今天要讲的是函数对像中一个很重要的属性--arguments. 相关阅读: javascript 函数详解1 -- 概述 javascrip ...

  8. ViewPager 详解(二)---详解四大函数

    前言:上篇中我们讲解了如何快速实现了一个滑动页面,但问题在于,PageAdapter必须要重写的四个函数,它们都各有什么意义,在上节的函数内部为什么要这么实现,下面我们就结合Android的API说明 ...

  9. JavaScript严格模式详解

    转载自阮一峰的博客 Javascript 严格模式详解   作者: 阮一峰 一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict m ...

  10. JavaScript运行机制详解

    JavaScript运行机制详解   var test = function(){ alert("test"); } var test2 = function(){ alert(& ...

随机推荐

  1. 最简单的基于FFmpeg的内存读写的例子:内存播放器

    ===================================================== 最简单的基于FFmpeg的内存读写的例子系列文章列表: 最简单的基于FFmpeg的内存读写的 ...

  2. [Error]Can't install RMagick 2.13.4. You must have ImageMagick 6.4.9 or later.

    gem 安装ruby插件的时候 出现了一个错误 Installing rmagick 2.13.4 with native extensions Gem::Installer::ExtensionBu ...

  3. React Native运行原理解析

    Facebook 于2015年9月15日推出react native for Android 版本, 加上2014年底已经开源的IOS版本,至此RN (react-native)真正成为跨平台的客户端 ...

  4. windows下git库的ssh连接,使用public key的方法

    在windows下进行项目开发,使用git,通过ssh方式与git库连接,而ssh方式用public key实现连接. 首先需要下载mygit,安装后使用git bash.git bash(有GUI界 ...

  5. DFS迷宫递归所有路径 新手入门

    这篇文章写给自己以后复习和个个入门朋友:提示同学们一定耐心看完解释 哪怕看得很难受,我是新手我懂大家的心烦.看完后慢慢体会代码 我们假设迷宫为如下状况:         {0,0,1,0}       ...

  6. 认识一下Android 事件分发机制

    1.引子 由于android是采用分层布局(可以想象成PS时的图层概念一样),这样才可以在有限大小的手机屏幕上完成一些复杂的操作.当手指点击屏幕开始,这些动作在各层之间如何传递?就引出了Android ...

  7. 【UML 建模】UML入门 之 交互图 -- 时序图 协作图详解

    . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/17927131 . 动态图概念 : 从静态图中抽取瞬间值 ...

  8. 【Unity插件】LitJson杂谈

    距离上一次更新博客已有一段时间了,一实习就懒了,嘿嘿.这次谈一下在实习里新碰到的一个Unity插件--LitJson(也可以去官网下载最新版). 开场白 LitJson是一个开源项目,比较小巧轻便,安 ...

  9. Locally Weighted Linear Regression 局部加权线性回归-R实现

      局部加权线性回归  [转载时请注明来源]:http://www.cnblogs.com/runner-ljt/ Ljt 作为一个初学者,水平有限,欢迎交流指正. 线性回归容易出现过拟合或欠拟合的问 ...

  10. Dynamics CRM ISV文件夹禁用后的解决方案

    众所周知微软在CRM2011的12补丁后取消了对ISV文件夹的支持,那我们自定义开发的一些web应用或者是想部署个服务该怎么办,有的选择了另开一个站点发布.我们以服务为例这样的另开站点的发布方式会导致 ...