4 函数
4.1 定义函数
- 定义方式一
function abs(x){
if(x>=0){
return x;
}else{}
return -x;
}
- 定义方式二,注意函数体结束后,末尾加分号,表示赋值语句结束
var abs = function (x){
if(x>=0){
return x;
}else{}
return -x;
};
//注意函数体结束后,末尾加分号
function (x){…}这是一个匿名函数,但是可以把结果赋值给abs,通过abs就可以调用函数!
-
函数调用
①正常按顺序传入参数
abs(10) 10 abs(-9) 9
以下两种情况为JavaScript特有,JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数:传入参数也不影响。
②不传入参数
abs() NaN
③传入实参个数多于形参个数
abs(10,9,8,56) 10
-
arguments关键字
arguments是一个JS免费赠送的一个关键字。
它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments
类似Array
但它不是一个Array
:
'use strict'
var abs = function (x){
console.log("x=>"+x);
for(let i=0;i<arguments.length;i++){
console.log("arg"+i+":"+arguments[i])
}
console.log("================");
if(x>=0){
return x;
}else{}
return -x;
};
abs(10,9,8,-9,-7,6)
x=>10
arg0:10
arg1:9
arg2:8
arg3:-9
arg4:-7
arg5:6
================
10
由于JavaScript函数允许接收任意个参数,于是我们就不得不用arguments
来获取所有参数:
- rest
获取多余参数,为了获取除了已定义参数a
、b
之外的参数,我们不得不用arguments
,并且循环要从索引2
开始以便排除前两个参数,这种写法很别扭,如果我们只是为了获得额外的rest
参数,能否采用一种简介的方式呢?===》ES6
中引入了rest
参数
'use strict'
function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}
foo()
a = undefined
b = undefined
[]
foo(1)
a = 1
b = undefined
[]
foo(2,3)
a = 2
b = 3
[]
foo(3,4,5)
9 a = 3
b = 4
[5]
function sum(...rest) {
let sum = 0;
for(let i = 0;i < rest.length;i++){
sum += rest[i]; }
return sum;
}
// 测试:
var i, args = [];
for (i=1; i<=100; i++) {
args.push(i);
}
if (sum() !== 0) {
console.log('测试失败: sum() = ' + sum());
} else if (sum(1) !== 1) {
console.log('测试失败: sum(1) = ' + sum(1));
} else if (sum(2, 3) !== 5) {
console.log('测试失败: sum(2, 3) = ' + sum(2, 3));
} else if (sum.apply(null, args) !== 5050) {
console.log('测试失败: sum(1, 2, 3, ..., 100) = ' + sum.apply(null, args));
} else {
console.log('测试通过!');
}
-
前面我们讲到了JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑:
-
JavaScript引擎有一个在行末自动添加分号的机制,这可能让你栽到return语句的一个大坑,所以不要不要不要将return和你需要返回的值分行书写。
4.2 变量的作用域
在JavaScript中,var定义变量实际是有作用域的。
在函数体中声明的变量,在函数体外无法使用~(非要实现的话,可以研究闭包,我还没研究呢)
'use strict'
function f() {
var x = 1;
x = x + 1;
}
x = x + 2; //Uncaught ReferenceError: x is not defined
下面这种情况,两个函数各自独立,当然不会相互影响
function f() {
var x = 1;
x = x + 1;
}
function f1() {
var x = "A";
x = x + 1;
}
嵌套时需要注意啦,内部函数可以调用外部函数的变量,反之不行
function f() {
var x = 1;
x = x + 1;
function f1() {
x = x + 2;
console.log("inner:"+x);
}
console.log("outer:"+x);
}
f();
JavaScript中函数查找变量由内向外,假设自身已经有该变量,则自动屏蔽外部同名的变量。
function f() {
var x = 1;
x = x + 1;
function f1() {
var x = "a";
x = x + 2;
console.log("inner:"+x);
}
console.log("outer:"+x);
}
f();
上面并没有显示inner,因为没有调用函数f1()
'use strict'
function f() {
var x = 1;
x = x + 1;
function f1() {
var x = "a";
x = x + 2;
console.log("inner:"+x);
}
f1();
console.log("outer:"+x);
}
f();
}
变量作用域提升
JavaScript的引擎自动提升了变量的作用域。
JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部:
'use strict';
function foo() {
var x = 'Hello, ' + y;
console.log(x);
var y = 'Bob';
}
foo();
虽然是strict模式,但语句var x = 'Hello, ' + y;
并不报错,原因是变量y
在稍后申明了。但是console.log
显示Hello, undefined
,说明变量y
的值为undefined
。这正是因为JavaScript引擎自动提升了变量y
的声明,但不会提升变量y
的赋值。
在JavaScript中遵守先声明后使用。
全局变量
'use strict';
var x = 1;
function f() {
console.log(x)
}
f()
console.log(x)
全局对象window
'use strict';
//全局变量&函数
var x = 1;
//以下四条等价
alert(x);
alert(window.x);//默认所有的全局变量都会绑定在window对象下
window.alert(x);
window.alert(window.x)
'use strict';
//全局变量
var x = 1;
window.alert(x);
var orig_alert = window.alert;
orig_alert(123)
window.alert = function () {//由JS函数的第二种定义方式可知,对象即函数,
}
window.alert("我不能弹出");
// 恢复alert,将之前的对象赋值给它即可
window.alert = orig_alert;
window.alert("我回来了")
JavaScript实际上只有一个全局作用域,任何变量(函数也可视为变量),如果在函数体内没有找到,就会向外查找,如果最后在全局作用域都没有找到,报错ReferenceError
规范 |
由于我们所有的全局变量都会绑定到window上,如果不同的js文件,使用了相同的全局变量,就会导致冲突。如何减少冲突呢?
'use strict';
//唯一全局变量
var MyFun = {};
MyFun.name = "tom";
MyFun.score = 89;
MyFun.sum = function (x,y) {
return x+y;
}
MyFun.sum(3,4);
把自己定义的变量放入自己定义的唯一全局变量空间中,降低不同js命名相同时造成的冲突问题。
jQuery=$,查 |
局部作用域let
'use strict';
function f() {
for(var i = 0;i<100;i++){
}
console.log(i+1);//出了作用域还能调用???
}
ES6中使用let关键字
'use strict';
function f() {
for(let i = 0;i<100;i++){
}
console.log(i+1);//Uncaught ReferenceError: i is not defined
}
一般建议使用let
常量const
由于var
和let
申明的是变量,如果要申明一个常量,在ES6之前是不行的,我们通常用全部大写的变量来表示“这是一个常量,不要修改它的值”:
ES6标准引入了新的关键字const
来定义常量,const
与let
都具有块级作用域:常量的值无法改变
'use strict';
const PI = 3.14;
console.log(PI);
PI = 3;// Uncaught TypeError: Assignment to constant variable.
变量作用域与解构赋值 - 廖雪峰的官方网站 (liaoxuefeng.com)
4.3 方法
定义方法
方法就是把函数放在对象里面,对象只有属性和方法两个东西
'use strict'
var tom = {
name:"tom",
birth:"2010",
//方法
age :function () {
//今年-出生年
var now = new Date().getFullYear();
return now - this.birth;
}
}
//属性
tom.name
//方法,方法一定要带括号。
tom.age()
this的含义是什么呢?
'use strict'
function getAge() {
//今年-出生年
var now = new Date().getFullYear();
return now - this.birth;
}
var tom = {
name:"tom",
birth:"2010",
//方法
age :getAge
}
//tom.age()
//11
//getAge() Uncaught TypeError: Cannot read properties of undefined (reading 'birth')
//this的默认对象指向了window
this是无法指向的,默认指向调用它的那个对象;
apply
在js中可以控制this指向
getAge.apply(tom,[])
11
5 内部对象
标准对象
typeof 123
'number'
typeof '123'
'string'
typeof true
'boolean'
typeof NaN
'number'
typeof []
'object'
typeof{}
'object'
typeof Math.abs
'function'
typeof undefined
'undefined'
5.1 Date
基本使用
'use strict'
var now = new Date();
//年月日 星期几 时分秒 时间戳
now.getFullYear();
now.getMonth();
now.getDate()
now.getDay();
now.getHours()
now.getMinutes();
now.getSeconds();
now.getTime();
new Date(1633855548347)
Sun Oct 10 2021 16:45:48 GMT+0800 (China Standard Time)
转换
now.toGMTString()//调用的是方法,而非属性
'Sun, 10 Oct 2021 08:45:48 GMT'
now.toDateString()
'Sun Oct 10 2021'
now.toLocaleString()
'10/10/2021, 4:45:48 PM'
now.toLocaleDateString()
'10/10/2021'
5.2 JSON
JSON是什么?
早期,所有数据传输习惯使用xml文件
- JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。
- 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
- 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
在JavaScript一切皆对象,任何js支持的类型都可以用JSON来表示;number,string…
格式:
- 对象,Map都使用{}
- 数组,set都使用[]
- 所有键值对都使用 key:value
JSON和JS对象的转换
'use strict'
let user = {
name : "tom",
age : 3,
gender : "male"
}
//对象转化为JSON字符串
let jsonuser = JSON.stringify(user);
//json字符串转化为对象
let user1 = JSON.parse(jsonuser);
let user2 = JSON.parse('{"name":"tom","age":3,"gender":"male"}');
// typeof user
// 'object'
// user
// {name: 'tom', age: 3, gender: 'male'}
// jsonuser
// '{"name":"tom","age":3,"gender":"male"}'
// user1
// {name: 'tom', age: 3, gender: 'male'}
// user2
// {name: 'tom', age: 3, gender: 'male'}
JSON和JS字符串有啥区别呢?
let obj = {
'a':"hello",
b:"hellob"
};
let jsonstring = JSON.stringify(obj);
//jsonstring = '{"a":"hello","b":"hellob"}'
5.3 Ajax
- 原生的js写法,xhr异步请求
- jQuery封装好的方法${#name}.ajax(""),
- axios 请求
6. 面向对象
原型对象
JavaScript,Java,c#…面向对象;
JavaScript有些区别
类: 模板 原型对象(JavaScript)
对象: 具体的实例
在JavaScript中需要转换一下思路?????、
原型:
'use strict'
let user = {
name : "tom",
age : 3,
gender : "male",
run:function () {
console.log(this.name+" is running....")
}
}
let xiaoming = {
name: "xiaoming"
}
user.run()
tom is running....
xiaoming.run()
Uncaught TypeError: xiaoming.run is not a function
开始魔幻了
'use strict'
let user = {
name : "tom",
age : 3,
gender : "male",
run:function () {
console.log(this.name+" is running....")
}
};
let xiaoming = {
name: "xiaoming"
};
xiaoming.__proto__ = user;
user.run()
tom is running....
xiaoming.run()
xiaoming is running....
在加了xiaoming.__proto__ = user
;相当于继承了user的方法和不重名属性,
再来看以下例子
'use strict'
let user = {
name : "tom",
age : 3,
gender : "male",
run:function () {
console.log(this.name+" is running....")
}
};
let xiaoming = {
name: "xiaoming"
};
xiaoming.__proto__ = user;
let Bird = {
fly:function () {
console.log(this.name+" is flying....")
}
}
//小明从人变成鸟
xiaoming.__proto__ = Bird;
xiaoming.fly()
xiaoming is flying....
xiaoming.run()
Uncaught TypeError: xiaoming.run is not a function
class继承
class
关键字是在ES6引入的,
所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法:有的时候我们想要在所有已经存在的对象添加新的属性或方法。另外,有时候我们想要在对象的构造函数中添加属性或方法。使用 prototype 属性就可以给对象的构造函数添加新的属性:当然我们也可以使用 prototype 属性就可以给对象的构造函数添加新的方法:
function Student(name) {
this.name = name;
}
//给student新增一个方法
Student.prototype.hello = function () {
console.log("hello")
}
Student.prototype.age =13;
let xm = new Student("xiaoming");
ES6之后,类似于java语法
//ES6之后
class Person{
constructor(name) {
this.name = name;
}
hello(){
alert("hello")
}
}
let xl = new Person("xiaoli");
- 定义一个类 ,属性。方法
//ES6之后
class Person{
constructor(name) {
this.name = name;
}
hello(){
alert("hello")
}
}
let xl = new Person("xiaoli");
- 继承
//ES6之后
class Student{
constructor(name) {
this.name = name;
}
hello(){
alert("hello")
}
}
class Pupil extends Student{
constructor(name,age,grade) {
super();
this.age = age;
this.grade = grade;
}
hello() {
console.log("我是一个小学生,我叫"+name+"我"+this.age+"岁了,正在上"+this.grade+"年级")
}
}
let xl = new Pupil("xiaoli",6,1);
let tom = new Pupil("tom");
xl.age
6
xl.hello()
我是一个小学生,我叫我6岁了,正在上1年级
tom.age
tom.hello()
我是一个小学生,我叫我undefined岁了,正在上undefined年级
原型链
无限套娃
javascript——原型与原型链 - 雅昕 - 博客园 (cnblogs.com)
7. 操作BOM
浏览器介绍
JavaScript 和 浏览器 的关系?
浏览器中有JavaScript引擎???????????????
DOM是一套操作HTML标签的API(接口/方法/属性)
BOM是一套操作浏览器的API(接口/方法/属性)
常见的BOM对象
- window:代表整个浏览器窗口(window是BOM中的一个对象,并且是顶级的对象)
- Navigator :代表浏览器当前的信息,通过Navigator我们可以获取用户当前使用的是什么浏览器
- Location: 代表浏览器当前的地址信息,通过Location我们可以获取或者设置当前的地址信息
- History:代表浏览器的历史信息,通过History我们可以实现上一步/刷新/下一步操作(出于
对用户的隐私考虑,我们只能拿到当前的浏览记录,不能拿到所有的历史记录) - Screen:代表用户的屏幕信息
来源:https://www.jianshu.com/p/eac7f9dc3b17
BOM:浏览器对象模型
- IE 6~11
- Chrome
- Safari
- FireFox Linux默认浏览器
- Opera
三方浏览器
- QQ浏览器
- 360浏览器
window对象(重要)
window 代表 浏览器窗口
window.innerWidth
459
window.outerWidth
1440
window.innerHeight
789
window.outerHeight
860
window.innerWidth
627
window.innerWidth
970
window.innerWidth
1189
//内外部高度和宽度,可以调整浏览器窗口再测试,看看有什么变化
Navigator
Navigator,封装了浏览器的信息
navigator.appName
'Netscape'
navigator.appVersion
'5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38'
navigator.cookieEnabled
true
navigator.userAgent
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Edg/94.0.992.38'
navigator.platform
'Win32'
navigator.usb
USB {onconnect: null, ondisconnect: null}
大多数时候,我们不会使用navigator
对象,因为会被人为修改!
不建议使用这些属性来编写代码,因为会被修改!!!!!
screen
screen.width
1440
screen.height
900
screen.availHeight
860
screen.colorDepth
24
screen.constructor
ƒ Screen() { [native code] }
screen.orientation
ScreenOrientation {angle: 0, type: 'landscape-primary', onchange: null}
screen.pixelDepth
24
location(重要)
location代表当前页面的URL信息
location.host//主机
'www.baidu.com'
location.hostname
'www.baidu.com'
location.protocol//协议
'https:'
location.reload()//刷新网页
//设置新地址
location.assign('https://www.bilibili.com/')
location
Location {ancestorOrigins: DOMStringList, href: 'https://www.baidu.com/', origin: 'https://www.baidu.com', protocol: 'https:', host: 'www.baidu.com', …}ancestorOrigins: DOMStringList {length: 0}assign: ƒ assign()hash: ""host: "www.baidu.com"hostname: "www.baidu.com"href: "https://www.baidu.com/"origin: "https://www.baidu.com"pathname: "/"port: ""protocol: "https:"reload: ƒ reload()replace: ƒ replace()search: ""toString: ƒ toString()valueOf: ƒ valueOf()Symbol(Symbol.toPrimitive): undefined[[Prototype]]: Location
document
document当前文档信息,HTML DOM文档树
document.location
Location {ancestorOrigins: DOMStringList, href: 'https://www.bilibili.com/', origin: 'https://www.bilibili.com', protocol: 'https:', host: 'www.bilibili.com', …}
document.timeline
DocumentTimeline {currentTime: 118083.226}
document.title
'哔哩哔哩 (゜-゜)つロ 干杯~-bilibili'
document.title="tom and jerry"
'tom and jerry'
获取具体的文档树节点
<dl id="first">
<dt>java</dt>
<dt>javaSE</dt>
<dt>javaEE</dt>
<dt>javaME</dt>
</dl>
<script>
let byId = document.getElementById("first");
</script>
获取cookie
document.cookie
劫持 cookie 原理
www.taobao.com
<script src="aa.js">
</script>
<!-- 恶意人员,获取你的cookie上传到他的服务器,信息泄露 -->
服务器端可以设置:httpOnly()
history(不建议使用)
history 获取浏览器的历史记录
history.back()//后退
history.forward()//前进
8. 操作DOM对象(重点)
DOM 文档对象模板
浏览器网页就是一个DOM树型结构
- 更新:更新DOM节点
- 遍历DOM节点:得到DOM节点
- 删除:删除一个DOM节点
- 添加:添加一个DOM节点
要操作一个DOM节点,就必须要先获得这个DOM节点
<h1>标题一</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
<script>
//对应CSS的选择器
let h = document.getElementsByTagName('h1');
let p1 = document.getElementById('p1');
let p2 = document.getElementsByClassName("p2");
</script>
<div id="father">
<h1>标题一</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
</div>
<script>
//对应CSS的选择器
let h = document.getElementsByTagName('h1');
let p1 = document.getElementById('p1');
let p2 = document.getElementsByClassName("p2");
let father = document.getElementById("father");
father.firstChild;//获取父节点下的所有子节点
father.lastChild;
p1.nextSibling;//下一个节点
p1.previousSibling;//前一个节点
father
<div id="father">…</div>
father.childElementCount
3
father.children
HTMLCollection(3) [h1, p#p1, p.p2, p1: p#p1]
father.firstElementChild
<h1>标题一</h1>
father.lastElementChild
<p class="p2">p2</p>
father.lastChild
#textassignedSlot: nullbaseURI: "http://localhost:63342/javascript/demo03/1.%E8%8E%B7%E5%BE%97DOM%E8%8A%82%E7%82%B9.html?_ijt=p39f8krn744pks20rt8fk5fgev"childNodes: NodeList []data: "\n"firstChild: nullisConnected: truelastChild: nulllength: 1nextElementSibling: nullnextSibling: nullnodeName: "#text"nodeType: 3nodeValue: "\n"ownerDocument: documentparentElement: div#fatherparentNode: div#fatherpreviousElementSibling: p.p2previousSibling: p.p2textContent: "\n"wholeText: "\n"[[Prototype]]: Text
p1.previousElementSibling
<h1>标题一</h1>
p1.previousSibling
#text
p1.nextElementSibling
<p class="p2">p2</p>
这是原生代码,之后会使用jQuery实现
更新节点
<div id="first">
</div>
<script>
let first = document.getElementById('first');
first.innerText = 456;
</script>
操作文本
first.innerText = 456
修改文本值first.innerHTML = '<strong>123</strong>'
可以解析HTML文本标签
操作css
first.style.color = "red"
'red'
first.style.fontSize = "30px"
'30px'
first.style.padding = "2em"
'2em'
删除节点
删除节点的步骤:先获取父节点,再通过父节点删除自己
<div id="father">
<h1>标题一</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
</div>
<script>
let self = document.getElementById('p1');
let father = self.parentElement;
father.removeChild(p1)
//删除是一个动态的过程
father.removeChild(father.children[0])
father.removeChild(father.children[1])//Uncaught TypeError: Failed to execute 'removeChild' on 'Node': parameter 1 is not of type 'Node'.
at <anonymous>:1:8
father.removeChild(father.children[2])
</script>
注意:删除节点的时候,children是在时刻变化的,删除节点的时候需要时刻注意。
插入节点
我们获得一个DOM节点,如果这个节点是空的,我们可以通过innerHTML 就可以追加一个元素,但是如果DOM节点已经有元素存在,匿名这样操作就会导致元素被覆盖。
追加
<p id="js">JavaScript</p>
<div id="list">
<p id="se">JavaSE</p>
<p id="ee">JavaEE</p>
<p id="me">JavaME</p>
</div>
<script>
let js = document.getElementById('js');
let list = document.getElementById('list');
list.append(js);//追加到后面
</script>
创建一个新的标签,实现插入
<p id="js">JavaScript</p>
<div id="list">
<p id="se">JavaSE</p>
<p id="ee">JavaEE</p>
<p id="me">JavaME</p>
</div>
<script>
let js = document.getElementById('js');//已存在的节点
let list = document.getElementById('list');
//通过JS创建一个新的节点
let newP = document.createElement('p');
//newP.id = "newP";
newP.innerHTML = '<p id="newP">万事不离,hello,world</p>';
//创建一个标签节点,通过这个属性,可以设置任意的值
let script = document.createElement('script');
script.setAttribute('text','text/javascript')
//创建一个style标签
let myStyle = document.createElement('style');
myStyle.setAttribute('type','text/css');
myStyle.innerHTML = 'body{background-color: burlywood;}'
let head = document.getElementsByTagName('head');
head[0].append(myStyle);
</script>
insert,前面插入insertBefore(newNode,oldNode),后面插入append,此处插入之后变为子节点。
<p id="js">JavaScript</p>
<div id="list">
<p id="se">JavaSE</p>
<p id="ee">JavaEE</p>
<p id="me">JavaME</p>
</div>
<script>
let js = document.getElementById('js');//已存在的节点
let list = document.getElementById('list');
let ee = document.getElementById('ee');
list.insertBefore(js,ee);//将js插入到ee前面
let p = document.createElement('p');
p.id = 'newP';
p.innerText = "hello,java";
ee.append(p)
</script>