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.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();
本质:查看原型对象

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

4、事件
- 事件 (Event) 是 JavaScript 应用跳动的心脏 ,进行交互,使网页动起来。当我们与浏览器中 Web 页面进行某些类型的交互时,事件就发生了。
- 事件可能是用户在某些内容上的点击、鼠标经过某个特定元素或按下键盘上的某些按键。 事件还可能是 Web 浏览器中发生的事情,比如说某个 Web 页面加载完成,或者是用户滚动窗口或改变窗口大小。
- 通过使用 JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应。
4.1、作用
-
验证用户输入的数据。
-
增加页面的动感效果。
-
增强用户的体验度
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级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。
首先发生的是事件捕获阶段,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

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

浙公网安备 33010602011771号