JavaScript函数02

目录

  • 常用事件和事件处理函数
  • 递归函数
  • 函数的属性和方法
  • 函数的作用域

常用事件和事件处理函数

事件参考--MDN
DOM当中,存在有很多的事件,通过这些事件,我们能够很好地监听用户在浏览器网页当中的一些动作,并且根据这些动作来设置相关的事件处理函数。

我们可以将事件分成三个部分,事件源,事件,以及事件发生后执行的动作。
对应着元素、事件以及事件处理函数。
例如:

按钮.单击= function () {事件发生后执行的动作} 

例如我们在网页中有一个按钮,当用户点击按钮的时候我们让网页弹出弹窗,弹出信息为hello,world。

具体代码可以如下:

// 找到按钮
var oBtn = document.getElementById('btn');
// 绑定事件
oBtn.onclick = function () { // 事件处理函数
    // 当用户发生点击操作之后弹出弹窗
    alert('hello,world');
};

在上面的代码中,我们通过给一个元素绑定单击事件,并且添加了事件处理函数。
一旦用户点击了按钮,那么就立刻会弹出弹窗。
当然,我们除了上面的写法以外,对于给一个元素绑定事件和事件处理函数的写法还可以换做另外一种形式:

<button id="btn" onclick="sayHello()">点击</button>
<script >
function sayHello(){
    alert('hello,world');
}
</script>

在上面的代码中,我们把事件和事件处理函数都绑定到了元素的身上,这样做的好处是能够省略了获取元素的过程。

在我们执行事件处理函数的时候,在某些时刻可能需要进行参数的传递,可以采用下面的几种方式。下面同样是点击问好的案例,但是问好的对象是对指定的人进行问好。

第一种方式:
demo:

<button onclick="sayHello('张三')">点击问好</button>
<script >
function sayHello(name){
    alert('hello,' + name );
}
</script>

第二种方式:
demo:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>

<input type="text" id="name">

<button id="btn">点击问好</button>


</body>
<script>
    // 获取input
    var oInput = document.getElementById('name');
    // 获取按钮
    var oBtn = document.getElementById('btn');

    // 点击按钮然后执行问好
    oBtn.onclick = function () {
        var userName = oInput.value;
        sayHello(userName);
    };
    function sayHello(name) {
        alert('hello,' + name);
    }
</script>
</html>

上面的代码中,我们可以在input中输入用户名,当用户点击按钮的时候,我们会将通过单击事件的事件
处理函数来调用sayHello方法,在这个方法中,获取用户输入的用户名,然后问好。

上面的代码基本上已经完成了我们的需求,但是我们仍然需要让我们的代码变得更加的完善。
例如,当我们点击按钮的时候,发现用户并没有输入用户名,我们该怎么办?

demo:

 // 点击按钮然后执行问好
    oBtn.onclick = function () {
        var userName = oInput.value;
        // 判断一下用户输入的内容是否为空
        if (userName === "") {
            alert("请输入用户名");
        }else {
            sayHello(userName);
        }
    };

在上面的代码中,当用户点击按钮调用了事件处理函数之后,我们进行了一个判断,判断用户输入的内容是否为空,如果为空,我们就可以继续提示用户,请输入用户名。
当然。上面的代码其实演示的仅仅是一种思路,代码仍然不是太严谨。

在我们的实际项目开发中,必须要让我们的代码尽可能的变得更加严谨,使我们的代码变得更加的强壮。

  • 鼠标事件:
    click 当用户按下并释放鼠标按键或其他方式“激活”元素时触发
    contextmenu 可以取消的事件,当上下文菜单即将出现时触发。当前浏览器在鼠标右击时显示上下文菜单
    dblclick 当用户双击鼠标时触发
    mousedown 当用户按下鼠标按键时触发
    mouseup 当用户释放鼠标按键时触发
    mousemove 当用户移动鼠标时触发
    mouseover 当鼠标进入元素时触发。relatedTarget(在IE中是
    fromElement)指的是鼠标来自的元素
    mouseout当鼠标离开元素时触发。relatedTarget(在IE中是toElement)指的是鼠标要去往的元素
    mouseenter类似mouseover,但不冒泡。IE将其引入,HTML5将其标准化,但尚未广泛实现
    mouseleave类似mouseout,但不冒泡。IE将其引入,HTML5将其标准化,但尚未广泛实现

  • 键盘事件
    keydown 按下
    keypress 点击
    keyup 抬起

  • 加载事件
    load 页面完全加载后会触发该事件
    error当加载失败后触发,所有可以触发load事件的元素,都可以触发该事件
    abort 元素加载中止时,(如加载过程中按下ESC键,停止加载),触发该事件,常用于图片加载 (只有IE支持)
    unloadload事件对应的是unload事件,该事件在文档被完全卸载后触发。刷新页面时,也会触发该事件。chrome/firefox/safari浏览器会阻止alert等对话框,IE浏览器会阻止console.log()等控制台显示。
    DOMContentLoaded 则在形成完整的DOM树之后就会触发,而不理会图像、javascript文件、CSS文件或其他资源是否下载完毕。(IE8不支持)

  • 表单事件
    submit 表单提交时触发,绑定给form元素
    reset 表单发生重置时触发,绑定为form元素
    blur 失去焦点时触发
    change 表单控制的内容发生改变时触发

  • 其他事件
    scroll 元素内部的内容滚动时触发
    resize 窗口尺寸发生变化时触发

并不是所有元素都具有所有事件

1.
box.onclick = function(){
    console.log(1)
};

2.
box.onclick = function fn2(){
    console.log(2)
};

3.在事件后,直接放函数名,别加()
box.onclick = fn3;
function fn3(){
    console.log(3)
    }

4.在这个函数内部,执行其他函数
box.onclick = function(){
    fn4()
};
function fn4(){
console.log(4)
}

根据执行方式不同的分类:
1.被事件直接执行的函数叫事件处理函数;
2.通过参数执行函数,这个作为参数的函数,叫回调函数

递归函数

当函数发生在函数体内自己调用自己这样的情况时,这个函数我们可以称为递归函数
例如下面的 demo,就是一个递归函数:

// 递归函数
function fib(num) {
  if (num === 0) return 0;
  if (num === 1) return 1;
  return fib(num - 2) + fib(num - 1);
}

console.log(fib(6)); // 8
F~{%05ANL6G)MC8SCK74AEE.png

在上面的demo中,通过递归函数计算了一下斐波那契数列
上面代码中,fib函数内部又调用了fib,计算得到斐波那契数列的第6个元素是8。

第一等公民的函数

js中,函数被称为一等公民
什么意思呢,就是说函数被看作是一种值,与其他的数据值(数值、字符串、布尔值等)地位相等。凡是可以使用值的地方都可以使用函数。
例如,我们可以将一个值赋值给一个变量,对应的,同样也可以将函数赋值给一个变量。

var fn = function () {};

同时,因为函数是一等公民,所以也可以将函数当做参数传递给其他函数,或者作为函数的返回结果。

function fn1() {}
function fn2(parm) {
  parm();
}

fn2(fn1);

函数的属性和方法

name 属性
函数的name属性返回函数的名字。

function fn1() {}
fn1.name; // "fn1"

如果是通过变量赋值定义的函数,那么name属性返回函数的名字。

var fn2 = function () {};
fn2.name; // fn2 

上面的案例中,函数是一个匿名函数,我们使用name属性就可以获得存储函数的变量的名称,但是一旦我们给匿名函数也定义了一个函数名,那么我们通过name属性查看到的将会是函数的名字而不是变量名。

var fn2 = function test () {};
fn2.name;  // test

需要注意的是,虽然打印出来的名称是test,但是真正的函数名还是fn2,test只是表达式的名字,并且test这个名字只能够在表达式内部使用。

我们可以通过name属性获取参数函数的的名字。

function fn1() {}

function fn2(parm){
    console.log(parm.name);
}
fn2(fn1); // fn1

在上面的代码中,在函数的内部通过name属性就获取了要传入的函数的名字。

length属性
函数的length属性返回函数预期传入的参数个数,即函数定义之中的参数个数也就是形参的个数。

function fn1(a,b) {

}
console.log(fn1.length);//2 

上面代码定义了空函数f,它的length属性就是定义时的参数个数。不管调用时输入了多少个参数,length属性始终等于2。

length属性提供了一种机制,判断定义时和调用时参数的差异,以便实现面向对象编程的“方法重载”(overload)。

toString()
函数的toString方法返回一个字符串,内容是函数的源码

function fn1(a,b) {
    console.log("hello,world!");
}
console.log(fn1.toString());

通过toString方法可以将函数全部的源码返回。
toString方法可以返回function(){native code}

console.log(Math.abs.toString()); // function abs() { [native code] }

上面代码中,Math.absjs提供的原生的函数,toString方法就会返回原生代码的提示。

需要注意的是,当我们使用toString方法时,函数内部的注释也会被返回

函数的作用域

定义
作用域(scope)指的是变量可以生存的范围或者说存在的范围。
在老版本的ES5的规范里,JS只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,在任何的地方都可以读取;另外一种
函数作用域,变量只在函数内部存在,在函数的外部没有办法访问到函数内部的变量。

ES6当中新增加了块级作用域

对于顶层函数来说,函数外部声明的变量就是全局变量(global variable),可以在函数内部读取。

var a = 10;
function fn1() {
    console.log(a);
}
fn1(); // 10

上面的代码中,我们在函数的外部声明了一个变量,我们在函数的内部也读取到了全局变量的值。

在函数内部定义的变量,在函数的外部则无法读取,我们将其称为局部变量(local variable)。

function t_fn1() {
    var a = 10;
}
console.log(a); // ReferenceError: a is not defined 引用错误:未定义

上面的案例中,我们尝试从函数的外部访问函数内部的局部变量(local variable),发现js提示我们变量没有定义,访问失败。

函数内部定义的局部变量,会覆盖掉函数外部定义的同名的全局变量

例如:

var x = 10;
function fn1(){
    var x = 20;
    console.log(x);
}
fn1(); // 20

在上面的代码中,变量x同时在函数外部和函数内部定义。结果显而易见,函数内部的变量x覆盖了函数外部的变量x。
但是需要注意的是,这种覆盖的变量,生效范围只能是在函数内部,如果出了函数,则变量x恢复到本身的10。

例如:

var x = 10;
function fn1() {
    var x = 20;
    console.log(x);
}
fn1(); // 20
console.log(x); // 10

在上面的代码中,我们在函数内部访问变量,x的值输出为函数内部变量的值20,而当出了函数,我们再来访问,你会发现变量x的值仍然为10。
所以说函数内部变量的覆盖仅停留于函数内部,出了函数就会恢复过来。

注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量

if (true) {
  var x = 5;
}
console.log(x);  // 5
函数内部的变量提升

全局作用域一样,函数作用域内部也会产生变量提升现象。var命令声明的变量,不管在什么位置,变量声明都会提升到函数体的头部。

例如:

function fn1() {
    var x = 2;
    if (x > 3){
      var tmp = x - 100;
      x = x + 1;
    }
}

上面的代码并没有什么实际的意义,但是这样一段代码在发生变量提升之后等同于下面的代码:

function fn1() {
  var x,tmp;
  x = 2;
  if(x > 3) {
    tmp = x - 100;
    x = x + 1;
  }
}
函数本身的作用域

上面我们说过,函数js当中是一等公民。本质上来讲也就是一个能够运行的值。So,函数既然是一个值,那么函数也就有着自己的作用域。
函数的作用域与变量相同,就是其声明时所在的作用域,一定要注意的是,函数的作用域与其运行时所在的作用域没有关系。

例如:

var a = 1;
var x = function () {
  console.log(a);
};
// 函数x在全局作用域环境下创建,所以说x函数的作用域就是全局,虽然x函数是在函数f中调用,但是作用域仍然是在全局。
function f() {
  var a = 2;
  x();
}

f() // 1

在上面的代码中,函数x是在全局作用域声明的函数,所以此时函数的作用域被绑定到全局。

函数的参数

函数的运行时,有时需要提供外部数据,我们提供不同的数据会导致不同的结果。这种外部的数据就叫做参数

// 定义一个函数,此时函数需要外部的数据a和b
function fn1(a,b) {
    return a + b;
}

// 调用函数并且传入数据
var x = 10;
var y = 20;

console.log(fn1(x,y));

在上面的案例中,a和b就是函数fn1的参数。在调用的时候,我们需要从外部传入数据来使用。

函数参数使用时需要注意的点:

  • 函数参数不是必须的,JavaScript允许省略参数。
function f1(a,b) {
    // 如果函数在运行时没有使用参数,那么参数也可以省略
    console.log('hello,world!');
}
f1(); // hello,world

上面的代码中,我们的函数在运行过程中并不需要参数a和b,所以我们在调用函数时候可以选择省略。但是需要注意的是,我们在函数运行的过程中如果定义了参数并且使用了参数而你却没有传入实参。那么函数体内使用该参数会返回undefined

function f1(a,b) {
    // console.log('hello,world!');
    console.log(a,b);// undefined
}
// 调用函数的时候并没有传入实参
f1();

如果通过length属性查看参数,那么length属性返回的值其实是形参的长度而不是实参的长度,也就意味着,只要你定义了函数
的形参,即使你在调用函数的时候没有传入实参,length属性也可以返回值。

  • 函数的参数不能只省略个别
    当我们调用函数的时候,我们不能省略只省略靠前的参数,而保留靠后的参数。如果一定要这么做,那么只有显示的传入undefined才可以。
function f1(a,b,c,d) {
    console.log(a,b,c,d);
}

// 调用函数的时候,如果想省略其中的个别参数,只能显示的传入undefined
// 而如果省略其中的一个参数就会报错。
f1(undefined,undefined,10,20);
  • 函数参数传递方式
    函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value)。这意味着,在函数体内修改参数值,不会影响到函数外部。

例如:

var a = 10;
function fn1(param) {
    param += 3;
    console.log('在函数内部的变量值为:' + param);
}
fn1(a); // 在函数内部的变量值为:13
console.log(a);// 10

上面代码中,变量p是一个原始类型的值,传入函数f的方式是传值传递。因此,在函数内部,p的值是原始值的拷贝,无论怎么修改,都不会影响到原始值。

但是,如果函数参数复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。

var obj = { p: 1 };

function f(o) {
  o.p = 2;
}
f(obj);

obj.p // 2

上面代码中,传入函数f的是参数对象obj的地址。因此,在函数内部修改obj的属性p,会影响到原始值。
注意,如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。

var obj = [1, 2, 3];

function f(o) {
  o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]

上面代码中,在函数f内部,参数对象obj被整个替换成另一个值。这时不会影响到原始值。这是因为,形式参数(o)的值实际是参数obj的地址,重新对o赋值导致o指向另一个地址,保存在原地址上的值当然不受影响。

  • 同名参数
    如果有同名的参数,则取最后出现的那个值。

例如:

function f1(a,a){
    console.log(a);
}

f1(1,2); // 2 

上面代码中,函数f有两个参数,且参数名都是a。取值的时候,以后面的a为准,即使后面的a没有值或被省略,也是以其为准。

function f(a, a) {
  console.log(a);
}

f(1) // undefined

调用函数f的时候,没有提供第二个参数,a的取值就变成了undefined。这时,如果要获得第一个a的值,可以使用arguments对象。

function f(a, a) {
  console.log(arguments[0]);
}

f(1) // 1
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 第3章 基本概念 3.1 语法 3.2 关键字和保留字 3.3 变量 3.4 数据类型 5种简单数据类型:Unde...
    RickCole阅读 5,106评论 0 21
  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,477评论 1 11
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,550评论 0 5
  • 函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 上面代码检查函数l...
    呼呼哥阅读 3,363评论 0 1
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,092评论 1 32