JS函数、内置对象、面向对象编程及事件(二)

1、函数

函数就是一段预先设置的功能代码块,可以反复调用,根据输入参数的不同,返回不同的值。函数也是对象

1.1、函数的定义

1、函数声明语句

function 函数名([参数列表]){
    
}
// 例如:
function abs(x){ 
    if(x >= 0) {
        return x;
    } else {
        return -x;
    }
} 
abs(-1);

该种方式定义的函数具有声明提升的效果

abs(-1);  
function abs(x){ 
    if(x >= 0) {
        return x;
    } else {
        return -x;
    }
} 
// 变量声明提升
console.log( a );  
var a = 2;

2、函数定义表达式

以表达式方式定义的函数,函数的名称是可以不需要的

var 变量名 = function ([参数列表]) {
    
}
变量名();
例如:
var fun = function(){
    console.log("Hello");
}
fun();

这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式,因为赋值语句的等号右侧只能放表达式。

3、Function构造函数

Function构造函数接收任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则列举出了新函数的参数。

var add = new Function('x','y','return (x + y)');
// 等同于
function add(x, y) {
    return (x + y);
}
add();

注意

  • js中的函数没有重载,同名的函数,会被后面的函数覆盖。

  • js中允许有不定数目的参数,后面介绍arguments对象

1.2、函数的参数、调用和return

参数

函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数,定义时的参 数称为形参,调用时的参数称为实参

  • 实参可以省略,那么对应形参为undefined
  • 若函数形参同名(一般不会这么干):在使用时以最后一个值为准。
  • 可以给参数默认值:当参数为特殊值时,可以赋予默认值。
  • 参数为值传递,传递副本 ;
  • 引用传递时传递地址,操作的是同一个对象。
  • 假设不存在参数,如何规避?
  • arguments:代表传递进来的所有的参数,是一个数组!
  • rest(es6新特性):获取除了已经定义的参数之外的所有参数~
// 调用函数时,实参可以省略,则对应形参为undefined
function add(a , b) {
 console.log(a + "+" + b + "=" + (a + b));
}
add(3,4,5)//3+4=7
add(1);//1+undefined=NaN
add();//undefined+undefined=NaN

// 若函数形参同名(一般不会这么干):在使用时以最后一个值为准
function add2(a , a) {
 console.log(a);
}
add2(1,2);

// 给参数默认值
function defaultValue(a){
 a = a || "a";
 return a;
}
console.log(defaultValue());
function f(a){
    
 //若参数a不为undefined或null,则取本身的值,否则给一个默认值
 (a !== undefined && a !== null) ? a = a : a = 1;
 return a;
}
console.log(f());

// 值传递
var num = 12;
function change(n) {
 n = 30;
}
change(num);
console.log(num);

// 引用传递
var obj = {name: "tom"};
function paramter(o) {
 o.name = 2;
}
paramter(obj);
console.log(obj.name);

// 给形参o赋予了新的数组
var obj2 = [1, 2, 3];
function paramter2(o){
 o = [2, 3, 4];
 o[1] = 3;
}
paramter2 (obj2);
console.log(obj2)

// 假设不存在参数,如何规避?
function abs(x){
    // typeof运算符是获取当前变量的类型
    // 手动抛出异常来判断
    if(typeof x !== 'number') {
        throw 'not a number';
    }
    if(x >= 0) {
        return x;
    } else {
        return -x;
    }
}

// arguments
// 问题: arguments包含所有的参数,我们有时候想使用多余的参数来进行附加操作。需要排除已有
// 参数~ 

function abs(x){
    console.log("x->" + x);
    for (let v of arguments) {
        console.log(v);
    }
    if(typeof x !== 'number') {
        throw 'not a number';
    }
    if(x >= 0) {
        return x;
    } else {
        return -x;
    }
}

// rest 
// 注意:rest只能在最后,并使用 ... 修饰
function test(a,b,...rest) {
    console.log("a->" + a);
    console.log("b->" + b);
    console.log(rest);
}

函数的调用

  • 常用调用方式

    •   // 存在返回值可以变量接收,若接收无返回值函数则为undefined。
        函数名([实参]);
      
  • 函数调用模式

    •   function add(a,b){
           return a+b;
        } 
        var sum = add(1,2) 
        console.log(sum); 
      
  • 方法调用模式

    •   var o = {
            m: function(){
                 console.log(1); 
           } 
        };
        o.m();
      

return

函数的执行可能会有返回值,需要使用return语句将结果返回。return 语句不是必需的,如果没有的话,该函数就不返回任何值,或者说返回 undefined。

作用:

  1. 在没有返回值的方法中,用来结束方法。
  2. 有返回值的方法中,一个是用来结束方法,一个是将值带给调用者。

1.3、变量的作用域

局部变量

1、假设在函数体中声明,则在函数体外不可以使用~

function f() {
    var x = 2;
}
x = x + 3; // Uncaught ReferenceError: x is not defined

2、如果两个函数使用了相同的变量名,只要在函数内部,就不冲突

function f() {
    var x = 2;
    x = x + 1;
    console.log(x);
}
function f2(){
    var x = 'a';
    x = x + 1;
    console.log(x);
}
f();
f2();

3、内部函数能访问外部函数的变量,反之不能

function f3() {
    var x = 1;

    function f4() {
        var y = x + 2; 
        console.log(y);
    }
    f4();
    var z = y + 1; // Uncaught ReferenceError: y is not defined
    console.log(z);
}
f3();

4、内部函数与外部函数定义同名变量,则在内部函数中外部变量将会被屏蔽

function f5() {
    var x = 1;

    function f6() {
        var x = 'a';
        console.log('inner:' + x); // inner:a
    }
    f6();
    console.log('outer:' + x); // outer:1
}
f5();

全局变量

// 全局变量
var x = 1;
function f() {
    console.log(x);
}
f();
console.log(x);

所有的全局变量都会自动绑定在window对象上

var msg = 'xxx';
alert(msg);
alert(window.msg);

小结

Javascript 实际上只有一个全局作用域,任何变量(函数也可以视为变量),假设没有在函数作用
范围内找到,就会向外查找,如果在全局作用域都没有找到,报错RefrenceError

规范

由于我们所有的全局变量都会绑定到我们的window上。如果不同的js文件,使用了相同的全局变
量,冲突~>如果能够减少冲突?

// 唯一全局变量
var connerApp = {};
// 定义全局变量
connerApp.name = 'conner';
connerApp.f1 = function (x,y) {
    return x + y;
}

把自己的代码全部放入自己定义的唯一空间名字中,降低全局命名冲突的问题~

局部作用域 let

function f() {
    for (var i = 0; i < 10; i++) {
        console.log(i);
    }
    console.log(i);  // 10 问题?这个i出了for循环这个代码快
}
f();

ES6 let关键字,解决局部作用域冲突问题

function f() {
    for (let i = 0; i < 10; i++) {
        console.log(i);
    }
    console.log(i);  // Uncaught ReferenceError: i is not defined
}
f();

常量 const

在ES6之前,怎么定义常量:只有用全部大写字母命名的变量就是常量;建议不要修改这样的值

var PI = 3.14;
console.log(PI);
PI = 314; // 这里是可以改变的
console.log(PI);

在ES6引入了常量关键字 const

const PI = 3.14;
console.log(PI);
PI = 314; // Uncaught TypeError: Assignment to constant variable.
console.log(PI);

1.4、方法

方法的定义

方法其实就是将函数放入对象中

let conner = {
    name:'conner',
    birthday: 2018,
    age: function () {
        return new Date().getFullYear() - this.birthday;
    }
};
// 注意调用方法时一定要加(),获取属性值不用
console.log(conner.age());

this是什么,将上面的代码拆开看看

let conner = {
    name:'conner',
    birthday: 2018,
    age: getAge
};

function getAge() {
    return new Date().getFullYear() - this.birthday;
}
console.log(conner.age()); // 正确
console.log(getAge()); // NaN 调用对象window

this是无法指向的,是默认指向调用它的那个对象,

appley

let conner = {
    name:'conner',
    birthday: 2018,
    age: getAge
};

function getAge() {
    return new Date().getFullYear() - this.birthday;
}
console.log(getAge.apply(conner,[])); // this指向conner,参数为空

2、内置对象

2.1、标准对象

typeof 12
"number"
typeof '12'
"string"
typeof NaN
"number"
typeof {}
"object"
typeof []
"object"
typeof null
"object"
typeof Math.abs()
"number"
typeof true
"boolean"
typeof undefined
"undefined"

2.2、Date

基本使用

let now = new Date();
undefined
console.log(now);
VM617:1 Thu Jun 17 2021 12:57:15 GMT+0800 (中国标准时间)
undefined
now.getFullYear(); // 年
2021
now.getMonth(); // 月  0~11代表月
5
now.getDate(); // 日
17
now.getDay(); // 星期几
4
now.getHours(); // 时
12
now.getMinutes(); // 分
57
now.getSeconds(); // 秒
15
now.getTime(); // 时间戳 从1970-1-1 00:00:00 到现在的毫秒数
1623905835267

转换

now = new Date(1623905835267);
Thu Jun 17 2021 12:57:15 GMT+0800 (中国标准时间)
now.toLocaleString() // 注意toLocaleString是方法,不是属性,调用需要带()
"2021/6/17下午12:57:15"
now.toGMTString()
"Thu, 17 Jun 2021 04:57:15 GMT"

2.3、JSON

什么是JSON?

  • JSON(JavaScriptObject Notation, JS 对象简谱) 是一种轻量级的数据交换格式(早期使用的是XML)。
  • 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
  • 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率

在JavaScript一切皆为对象、任何js支持的类型都可以用SON来表示; number,string...
格式:

  • 对象都用 {}
  • 数组都用 []
  • 所有的键值对都是用key:value

JSON与JS对象的转换

let user = {
    name:'conner',
    age:3,
    sex:"男"
};
// JS对象转JSON
let userJson = JSON.stringify(user);
console.log(userJson); // {"name":"conner","age":3,"sex":"男"}

// JSON转JS对象
let user2 = JSON.parse('{"name":"conner","age":3,"sex":"男"}');
console.log(user2); // {name: "conner", age: 3, sex: "男"}

JS对象与JSON的区别:JS对象中的属性名不用加引号,而JSON中需要

3、面向对象编程

javascript、Java、c# ... 面向对象; javascript有些区别!

  • 类:模板
  • 对象:具体的实例

在JavaScript这个需要大家换一下思维方式!

3.1、原型继承

let student = {
    name:'tim',
    age:3,
    run:function () {
        console.log(this.name + ' run.....');
    }
};

let conner = {
    name:'conner'
};

// 原型对象为student
conner.__proto__ = student;

conner.run();

let bird = {
    name:'xx',
    fly:function () {
        console.log(this.name + ' fly.....');
    }
};

// 原型对象为bird
conner.__proto__ = bird;
conner.fly();
// 相当于一个类
function Student(name) {
    this.name = name;
}

// 给Student新增一个方法
Student.prototype.hello = function () {
    alert('Hello ' + this.name);
};

let student = new Student('conner');
console.log(student.name);
student.hello();

3.2、class继承

class关键字是ES6引入的

1、定义一个类

// 类
class Student{
    // 构造器
    constructor(name) {
        // 定义属性
        this.name = name;
    }

    // 方法
    hello(){
        alert('Hello ' + this.name);
    }

}

// 使用
let student = new Student('conner');
student.hello();

2、继承

// 类
class Student{
    // 构造器
    constructor(name) {
        // 定义属性
        this.name = name;
    }

    // 方法
    hello(){
        alert('Hello ' + this.name);
    }

}

class MiddleStudent extends Student{
    constructor(name, grade) {
        super(name);
        this.grade = grade;
    }

    getGrade(){
        alert(this.grade);
    }

}

let middleStudent = new MiddleStudent('conner', 7);
middleStudent.getGrade();

本质:查看原型对象

image-20210617160511966

3.3、原型链

_proto_:这里不详细展开讲,有兴趣的可以自己查阅资料

image-20210617161108055

4、事件

  • 事件 (Event) 是 JavaScript 应用跳动的心脏 ,进行交互,使网页动起来。当我们与浏览器中 Web 页面进行某些类型的交互时,事件就发生了。
  • 事件可能是用户在某些内容上的点击、鼠标经过某个特定元素或按下键盘上的某些按键。 事件还可能是 Web 浏览器中发生的事情,比如说某个 Web 页面加载完成,或者是用户滚动窗口或改变窗口大小。
  • 通过使用 JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应。

4.1、作用

  1. 验证用户输入的数据。

  2. 增加页面的动感效果。

  3. 增强用户的体验度

4.2、事件中的几个名词

  • 事件源: 谁触发的事件
  • 事件名: 触发了什么事件
  • 事件监听: 谁管这个事情,谁监视?
  • 事件处理:发生了怎么办

示例:

闯红灯     事件源:车 ;   事件名: 闯红灯;    监听:摄像头、交警 ;      处理:扣分罚款
单击按钮   事件源:按钮;   事件名: 单击;     监听:窗口 ;             处理:执行函数

当我们用户在页面中进行的点击动作,鼠标移动的动作,网页页面加载完成的动作等,都可以称之为事件名称,即: click、mousemove、load 等都是事件名称,具体的执行代码处理,响应某个事件的函数。

<!-- load加载事件:当页面加载完毕后执行的事件 -->
<body onload="loadWindow();"></body>
<script>
    function loadWindow(){
        alert("加载窗体");
    }
</script>

4.3、事件类型

JavaScript可以处理的事件类型为:鼠标事件键盘事件HTML事件

Window 事件属性:针对 window 对象触发的事件(应用到标签)

Form 事件:由 HTML 表单内的动作触发的事件(应用到几乎所有 HTML 元素,但最常用在 form 元素中)

Keyboard 事件 : 键盘事件

Mouse 事件:由鼠标或类似用户动作触发的事件

Media 事件:由媒介(比如视频、图像和音频)触发的事件(适用于所有 HTML 元素,但常见于媒介元素中,比如<audio>、 <embed>、 <img>、 <object>以及<video>)

几个常用的事件

  • onload:当页面后图像加载后立即触发
  • onblur :元素失去焦点触发
  • onfocus :元素获得焦点触发
  • onclick :鼠标点击某个对象触发
  • onchange:用户改变域得内容触发
  • onmouseover:鼠标移动到某个元素上触发
  • onmouseout:鼠标从某个元素上离开触发
  • onkeyup:某个键盘得键被松开触发
  • onkeydown:某个键盘得键被按下触发

4.4、事件流和事件模型

我们的事件最后都有一个特定的事件源,暂且将事件源看做是HTML的某个元素,那么当一个HTML元素产生一个事件时,该事件会在元素节点与根节点之间按特定的顺序传播,路径所经过的节点都会受到该事件,这个传播过程称为 DOM事件流

事件顺序有两种类型:事件捕获事件冒泡

冒泡和捕获其实都是事件流的不同表现,这两者的产生是因为IE和Netscape两个大公司完全不同的事件流概念产生的。(事件流:是指页面接受事件的顺序)IE的事件流是事件冒泡,Netscape的事件流是事件捕获流。

事件冒泡

IE的事件流叫做事件冒泡,即事件开始时由最具体的元素接受,然后逐级向上传播到较为不具体的节点(文档)(从小到大)。例 如下面的:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>JavaScript</title>
    </head>
    <body>
        <div id="myDiv">Click me</div>
    </body>
</html>

如果点击了页面中<div>的元素,那么这个click事件会按照如下顺序传播:

1、<div>

2、<body>

3、<html>

4、\document

也就是说,click事件首先在div元素上发生,而这个元素就是我们单击的元素。然后,click事件沿DOM树向上传播, 在每一级节点上都会发生,直到传播到document对象。

所有现代浏览器都支持事件冒泡,但在具体实现上还是有一些差别。

事件捕获

Netscape提出的另一种事件流叫做事件捕获,事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件(从大到小)

事件捕获的用意在于在事件到达预定目标之前捕获它。还以前面的例子为例。那么单击元素就会按下列顺序触发click事件:

1、document

2、<html>

3、<body>

4、<div>

在事件捕获过程中,document对象首先接收到click事件,然后沿DOM树依次向下,一直传播到事件的实际目标, 即<div>元素。

虽然事件捕获是Netscape唯一支持的事件流模式,但很多主流浏览器目前也都支持这种事件流模型。尽管“DOM2级事件”规范要求事件应该从document对象开始时传播,但这些浏览器都是从window对象开始捕获的

DOM事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段处于目标阶段事件冒泡阶段

首先发生的是事件捕获阶段,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

image-20210618220935700

4.5、事件处理程序

事件就是用户或浏览器自身执行的某种动作。例如click、load和mouseover都是事件的名字,而响应某个事件的函数就叫做事件处理程序(或事件侦听器)。事件处理程序的名字以“on”开头,因此click事件的事件处理程序就是 onclick,为事件指定处理程序的方式有好几种。

HTML事件处理程序

某个元素支持的每种事件,都可以用一个与相应事件处理程序同名的HTML特性来指定。这个特性的值应该是能够执 行的JavaScript代码:

<input type="button" value="Press me" onclick="alert('thanks');" />

这样做得缺点:

  • 耦合度过高
  • 可能存在时差问题(当用户点击按钮时,处理函数还未加载到,此时处理函数是单独写的一段js代码)
  • 在不同的浏览器上可能会有不同的效果。

DOM0 级事件处理程序

通过JavaScript指定事件处理程序的传统方式,就是将一个函数赋值给一个事件处理程序属性。这种方式被所有现代浏览器所支持。这种方式首先必须取得一个要操作的对象的引用,每个元素都有自己的事件处理程序属性,这些属性通常全都小写,例如onclick,然后将这种属性的值设为一个函数,就可以指定事件处理程序了。例如:

<body>
    <button id="myBtn">按钮</button>
    <script type="text/javascript">
        var btn = document.getElementById('myBtn');
        btn.onclick = function(){
            console.log('you click a button');
       };
    </script>
</body>

以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理。而且,只能为同一个元素的同一个事件设定一个处理程序(覆盖),也可以通过删除DOM0级方法指定的事件处理程序,只要将属性值设为null即可:

btn.onclick = null;

DOM2 级事件处理程序

“DOM2级事件”定义了两个方法,用于处理指定和删除事件处理程序的操作:addEventListener()removeEventListener()

所有DOM节点都包含这两个方法,并且他们都接受3个参数:

  • 要处理的事件名
  • 作为事件处理程序的函数
  • 一个布尔值(一般不写)
    • 这个布尔值参数如果是true,则表示在捕获阶段调用事件处理程序;如果是false 则表示在冒泡阶段调用事件处理程序。
<body>
    <button id="myBtn">按钮</button>
    <script type="text/javascript">
        var btn = document.getElementById('myBtn');
        btn.addEventListener('click',function(){
            alert('you add a eventListener by DOM2')
       },false);
        btn.addEventListener('click',function(){
            alert('you add a eventListener by DOM2 again')
       },false);
       function thread(){
            alert('you add a eventListener by DOM2 第三次');
       }
        btn.addEventListener('click',thread,false);
        btn.removeEventListener('click',thread,false);
    </script>
</body>

这种方式可以为同一个元素的同一个事件添加多个处理函数。还可删除事件处理函数,注意,在删除的时候,不能删除匿名处理函数。

posted @ 2021-06-19 20:41  JavaConner  阅读(106)  评论(0)    收藏  举报