目录
1. 什么是JavaScript
1.1 概述
JavaScript是一门世界上最流行的脚本语言。
Java 和 Javascript的关系: 没有任何关系(类似于“雷锋”和“雷峰塔”的关系)
注:一个合格的后端开发人员,必须要精通Javascript。
1.2 JavaScript历史
ECMAScript:可以理解为是JavaScript的一个标准。
最新版本已经到es6版本~
但是大部分浏览器还只停留在支持es5代码上!
所以就会导致:开发环境 和 线上环境 的版本不一致的问题。
2. 快速入门
2.1 引入javaScript
1. 内部标签
<script>
alert('hello kaixuan');
</script>
2.外部引入
abc.js
//.....
test.html
<script src="abc.js"></script>
测试代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--script标签内, 写JavaScript代码-->
<script>
alert('hello kaixuan');
</script>
<!--外部引入-->
<!--注意: script标签必须成队出现-->
<script src="js/qj.js"></script>
<!--不用显示定义type,也默认就是javaScript-->
<script type="text/javascript"></script>
</head>
<body>
<!--JS代码也可以放在这个位置-->
</body>
</html>
2.2 基本语法入门
<!--javascript严格区分大小写-->
<script>
// 1.定义变量 变量类型(都是var) 变量名 = 变量值;
var num = 1; var zzz = 2;
//alert(num);
'helloworld' //这样写也是合法的
// 2.条件控制
var score = 79;
if(score > 60 && score <70){
alert("60~70");
}else if(score > 70 && score < 80){
alert("70~80");
}else{
alert("other");
}
//console.log(score) : 在浏览器的控制台打印变量! System.out.println();
</script>
浏览器必备调试须知:
2.3 数据类型
数值、文本、图形、音频、视频......
变量
var $a
number
js不区分小数和整数,统一是Number
123 //整数123
123.1 //浮点数123.1
1.123e3 //科学计数法
-99 //负数
NaN //not a number (也是Number类型的)
Infinity //表示无限大
字符串
'abc' "abc"
布尔值
true , false
逻辑运算
&& //两个都为真,结果为真
|| //一个为真, 结果为真
! //取反
比较运算
=
== //等于 (类型不一样,值一样, 也会判断为true)
=== //绝对等于(类型一样,值一样, 结果true)
123 == '123'
true
123 === '123'
false
这是一个JS的缺陷, 坚持不要使用 == 比较
须知:
- NaN === NaN返回的结果为false, 因为NaN和所有的数值都不相等,包括它自己。
- 只能通过isNaN(NaN)来判断这个数是否是NaN
浮点数问题:
console.log((1/3) === (1 - 2/3)) //结果为false
尽量避免使用浮点数进行运算, 存在精度问题!
Math.abs(1/3 - (1 - 2/3)) < 0.000000001
null 和 undefined
- null 空
- undefined 未定义
数组
Java的数组元素必须是相同类型的对象~, JS中不需要这样!
//保证代码的可读性, 尽量使用[]
var arr = [1, 2, 3, 4, 5, 'hello', null, true];
new Array(1, 12, 3, 4, 5, 'hello');
取数组下标,如果越界了,就会undefined
对象
对象是大括号,数组是中括号~~
每个属性之间使用逗号隔开,最后一个不需要添加
//Person person = new Person(1, 2, 3, 4, 5);
var person = {
name:"kaixuan",
age : 19,
tags : ['js', 'java', 'web', '...']
}
取对象的值
person.name
> "kaixuan"
person.age
19
2.4 严格检查模式
es6才支持严格检查模式,需设置好。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--
'use strict' : 严格检查模式,预防JavaScript的随意性导致的一些问题
必须写在javaScript的第一行!且使用的前提是IDEA需要设置支持ES6语法
局部变量建议都使用let去定义~
-->
<script>
//全局变量
i = 1; //变量i的类型没有指定却可以正常存活,而且i默认还是全局变量(我们不希望这是个全局变量)
'use strict';
//局部变量
let i = 1;
</script>
</head>
<body>
</body>
</html>
3.数据类型
3.1 字符串
1、正常字符串我们使用单引号或者双引号包裹。
2、注意转义字符\
\'
\n
\t
\u4e2d \u#### : Unicode字符
\x41 AscII字符
3、多行字符串编写
//使用着重号 实现多行字符串编写
var msg =`kaiuxan
dabing
aiping
runze
xiaowan
`
4、模版字符串
//使用着重号
let name = "kaixuan"
let age = 19;
let msg = `你好, ${name}`
5、字符串长度
str.length
6、字符串的不可变性
7、大小写转换
//注意,这里是方法,不是属性
student.toUpperCase()
student.toLowerCase()
8、student.indexOf('t') //通过字符串元素获得下标索引,索引从0开始
9、substring
student.substring(1) //从第一个字符串截取到最后一个字符串
student.substring(1, 3) //[1, 3) 左闭右开
3.2 数组
数组可以包含任意的数据类型
var arr = [1, 2, 3, 4, 5, 6]; //通过下标取值和赋值
arr[0]
arr[0]=1
1、长度
arr.length
注:数组的长度可以动态(自行)指定,如果赋值过小,元素就会丢失,
6、
2、indexOf, 通过元素获得下标索引
arr.indexOf(2) //找元素2对应的下标
可见,字符串的"1"和数字1是不同的。
3、slice() : 截取Array的一部分,返回一个新数组。类似于String中的substring
4、push(), pop() : 在尾部插入和弹出
push : 压入元素到尾部
pop : 弹出尾部的一个元素
5、unshift(), shift() : 在头部插入和弹出
unshift : 压入到头部
shift : 弹出头部的一个元素
6、排序sort()
(3) ['B', 'C', 'A']
arr.sort()
(3) ['A', 'B', 'C']
7、元素反转 reverse()
(3) ['A', 'B', 'C']
arr.reverse()
(3) ['C', 'B', 'A']
8、concat()
(3) ['C', 'B', 'A']
arr.concat([1, 2, 3])
(6) ['C', 'B', 'A', 1, 2, 3]
arr
(3) ['C', 'B', 'A'] //原数组没有改变
注: concat()方法并没有修改数组, 只是会返回一个新的数组。
9、连接符join
打印拼接数组, 使用特定的字符串连接
(3) ['C', 'B', 'A']
arr.join('-')
'C-B-A'
arr
(3) ['C', 'B', 'A'] //原数组的值没有发生改变
10、多维数组
arr = [[1, 2], [3, 4], ["5", "6"]];
arr[1][1]
4
3.3 对象
若干个键值对
var 对象名 = {
属性名: 属性值,
属性名: 属性值,
属性名: 属性值
}
//定义了一个person对象, 它有四个属性!
var person = {
name : "kaixuan",
age : 19,
email : "672836466@qq.com",
score : 96
}
JS对象中,{......}表示一个对象, 键值对描述属性xxxx : xxxx , 多个属性之间使用逗号隔开,最后一个属性不加逗号!
JavaScript中所有的键都是字符串,值是任意对象!
1、对象赋值
person.name = "dabing"
"dabing"
person.name
"dabing"
2、使用一个不存在的对象属性,不会报错! undefined
person.haha
undefined
3、动态的删除属性, 通过delete删除对象的属性
delete person.name
true
//再次查看person对象时name属性和其值就被删掉了
person
{age: 19, email: '672836466@qq.com', score: 96}
4、动态的添加,直接给新的属性添加值即可
person.haha = "haha"
"haha"
5、判断属性值是否在这个对象中! xxx in xxx!
'age' in person
true
//继承
'toString' in person
true
6、判断一个属性是否是这个对象自身拥有的: hasOwnProperty()
person.hasOwnProperty('toString')
false
person.hasOwnProperty('age')
true
3.4 流程控制
if判断
var age = 4;
if(age < 3){
alert("haha~");
}else if(age < 5){
alert("kuwa~");
}else{
alert("kaixuan");
}
while循环, 避免程序死循环
var age = 9;
while (age < 100){
age++;
console.log(age);
}
for(let i = 1; i < 100; i++){
console.log(i);
}
forEach循环 (5.1引入)
var age = [12, 3, 4, 78, 99, 25];
//函数
age.forEach(function (value) {
console.log(value)
})
for ... in
//for(var index in object)
for (var num in age){
if(age.hasOwnProperty(num)){
console.log(age[num]);
}
}
3.5 Map和set
ES6的新特性~
Map :
var map = new Map([['tom', 100],['jack', 90], ['haha', 80]]);
var score= map.get('tom'); //通过key获得value
console.log(score);
map.set('admin', 123456); //添加或更新
map.delete('tom'); //更新
Set : 无序不重复的集合
var set = new Set([1, 2, 6, 2, 5, 2]); //set可以去重
set.add(10); //添加
set.delete(2); //删除
console.log(set.has(6)); //是否包含某个元素
3.6 iterator
es6新特性
遍历数组
//通过for...of实现 / for...in 遍历出来的是下标
var arr = [3, 4, 5]
for(var x of arr){
console.log(x)
}
遍历map
var map = new Map([["tom", 100], ["jack", 90], ["haha", 80]])
for(let x of map){
console.log(x);
}
遍历set
var set = new Set([5, 6, 7]);
for (let x of set){
console.log(x);
}
4. 函数
4.1 定义函数
定义方式一:
绝对值函数
function abs(x) { //JS中的变量类型都是var,所以形参类型干脆不写
if(x < 0){
x = -x;
}
return x;
}
一旦执行到return 代表函数结束, 返回结果!
如果没有执行return ,函数执行完也会返回结果, 结果就是undefined
定义方式二:
var abs = function(x){
if(x < 0){
x = -x;
}
return x;
}
function(x){....} 这是一个匿名函数。 但是可以把结果赋值给abs, 通过abs就可以调用函数!
方式一和方式二等价!
调用函数
abs(10) //10
abs(-10) //10
参数问题: JavaScript可以传任意个参数,也可以不传递参数~
参数传进来是否存在的问题
假设不存在参数,如何规避?
var abs = function(x){
//手动抛出异常来判断
if(typeof x !== 'number'){
throw 'Not a Number';
}
if(x >=0 ){
return x;
}else{
return -x;
}
}
arguments
arguments是一个JS免费赠送的关键字;
代表传递进来的所有的参数,是一个数组!
var abs = function(x){
console.log("x=>" + x);
for(var i = 0; i < arguments.length; i++){
console.log(arguments[i]);
}
if(x > 0){
return x;
}else{
return -x;
}
}
问题: arguments包含所有的参数, 我们有时候想使用多余的参数来进行附加操作。 需要排除已有的参数~
rest
rest
ES6 引入的新特性, 获取除了已经定义的参数之外的所有参数~...
function aaa(a, b, ... rest){
console.log("a=>" + a);
console.log("b=>" + b);
console.log(rest);
}
rest参数只能写在最后面, 必须用...标识。
4.2 变量的作用域
在 javascript 中,var 定义变量实际是有作用域的。
假设在函数体中声明, 则在函数体外不可以使用~ (非要想实现的话, 后面可以研究一下 "闭包")
function triumph(){
var x = 1;
x = x + 1;
}
x = x + 2;
</script> //Uncaught ReferenceError: x is not defined
如果两个函数使用了相同的变量名, 只要在函数内部,就不冲突
function kaixuan(){
var x = 1;
x = x + 1;
}
function kaixuan2(){
var x = 'A';
x = x + 1;
}
内部函数可以访问外部函数的成员,反之不行:
function kaixuan(){
var x = 1;
//内部函数可以访问外部函数的成员,反之则不行
function kaixuan2(){
var y = x + 1; //2
}
var z = y + 1; //Uncaught ReferenceError: y is not defined
}
假设,内部函数变量和外部函数变量重名:
function kaixuan(){
var x = 1;
function kaixuan2(){
var x = 'A';
console.log('inner' + x)
}
console.log('outer' + x);
kaixuan2();
}
kaixuan();
在JS中,函数查找变量从自身函数开始, 由内往外查找, 假设外部存在这个同名的函数变量, 则内部函数变量会屏蔽掉外部的函数变量。
提升变量的作用域
function kaixuan() {
var x = 'x' + y;
console.log(x);
var y = 'y';
}
kaixuan();
结果: xundefined
说明:js执行引擎自动提升了y的声明, 但是不会提升变量y的赋值。这个是在javascript建立之初就存在的特性。
全局函数
//全局变量
var x = 1
function f() {
console.log(x);
}
f();
console.log(x);
全局对象window
var x = 'xxx';
alert(x);
window.alert(x);
alert(window.x); //默认所有的全局变量,都会自动绑定在window对象下;
alert()这个函数本身也是一个window变量;
var x = 'xxx';
window.alert(x);
var old_alert = window.alert;
// old_alert(x);
window.alert = function () {
}; //相当于改变了alert本来的功能
window.alert(123);
//恢复
window.alert = old_alert;
window.alert(456);
JavaScript 实际上只有一个全局作用域,任何变量(函数也可以视为变量), 假设没有在函数作用范围内找到, 就会向外查找, 如果在全局作用域都没有找到,报错ReferenceError
规范
由于我们所有的全局变量都会绑定到我们的window上。 如果不同的js文件,使用了相同的全局变量,冲突——>如何能够减少冲突?
//唯一全局变量
var kuangApp = {};
//定义全局变量
kuangApp.name = 'kuangshen';
kuangApp.add = function (a, b) {
return a + b;
}
解决办法: 把自己的代码全部放入自己定义的唯一空间名字中, 降低全局命名冲突的问题! JQuery
局部作用域let
function aaa() {
for(var i = 0; i < 100; i++){
console.log(i);
}
console.log(i + 1); //问题:i出了这个作用域还可以使用
}
aaa();
可以看到上述代码段中的问题
ES6 let关键字, 解决局部作用域冲突的问题!
function aaa() {
for(let i = 0; i < 100; i++){
console.log(i);
}
console.log(i + 1); //Uncaught ReferenceError: i is not defined
}
aaa();
建议使用let去定义局部作用域的变量;
常量const
在ES6之前, 怎么定义常量: 只要用全大写字母命令的变量就是常量;建议不要修改这样的值;
var PI = '3.14';
console.log(PI);
PI = '213'; //可以改变这个值
console.log(PI);
在ES6引入了常量关键字const
const PI = '3.14'; //只读变量
console.log(PI);
PI = '123'; //Uncaught TypeError: Assignment to constant variable.
console.log(PI);
4.3 方法
定义方法
方法就是把函数放在对象的里面, 对象只有两个东西: 属性和方法
var kaixuan = {
name: '凯旋',
birth: 1998,
//方法
age:function(){
//今年 - 出生年份
var now = new Date().getFullYear();
return now - this.birth;
}
}
//属性
kaixuan.name
//方法,调用时一定要带()
kaixuan.age()
this代表什么? 拆开上面的代码来看看~
function getAge(){
//今年 - 出生年份
var now = new Date().getFullYear();
return now - this.birth;
}
var kaixuan = {
name: '凯旋',
birth: 1998,
//方法
age:getAge
}
//kaixuan.age() OK
//getAge() Nan
this是无法指向的, 是默认指向调用它的那个对象;
apply
在js中可以控制this指向!
function getAge(){
//今年 - 出生年份
var now = new Date().getFullYear();
return now - this.birth;
}
var kaixuan = {
name: '凯旋',
birth: 1998,
//方法
age:getAge
}
var dabing = {
name: '大冰',
birth: 1998,
age:getAge
}
getAge.apply(kaixuan, []); //this,指向了kaixuan 这个对象, 参数为空
4.4 闭包
4.4.1 垃圾回收机制
为了更好的理解闭包,需要先回顾一下垃圾回收机制(Garbage Collection)。
垃圾回收机制:JS中内存的分配和回收都是自动完成的, 内存在不使用的时候会被垃圾回收器自动回收。
内存的生命周期
JS环境中分配的内存, 一般有如下生命周期:
- 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存。
- 内存使用:即读写内存, 也就是使用变量、函数等。
- 内存回收:使用完毕, 由垃圾回收器自动回收不再使用的内存。
说明:
- 全局变量一般不会回收(关闭页面回收)
- 一般情况下局部变量的值, 不用了,会被自动回收掉。
内存泄漏:程序中分配的内存由于某种原因程序未释放或无法释放叫做内存泄漏。
两种常见的浏览器垃圾回收算法:引用计数法和标记清除法
引用计数法:
IE采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否由指向它的引用,没有引用了就回收对象。
算法:
- 跟踪记录被引用的次数
- 如果被引用了一次,那么就记录次数1,多次引用就会累加
- 如果减少一个引用就减1
- 如果引用次数是0, 则释放内存。
算法执行过程如下图所示:
引用计数法存在的问题:嵌套引用(循环引用)
如果两个对象相互引用,尽管他们已经不再使用,垃圾回收器不会进行回收,导致内存泄漏。
function(){
let o1 = {}
let o2 = {}
o1.a = o2
o2.a = o1
return '引用计数无法回收'
}
fn()
因为他们的引用次数永远不会是0。这样的相互引用如果说是大量存在的话就会导致大量的内存泄漏。(图示如下所示)
标记清除法:
现代的浏览器已经不再使用引用计数法了。
现代的浏览器通用的大多是基于标记清除法的某些改进算法,总体思想都是一致的。
核心:
- 标记清除法将“不再使用的对象”定义为“无法到达的对象”
- 就是从根部(在JS中就是全局变量)出发定时扫描内存中的对象。 凡是能从根部到达的对象,都是还需要使用的。
- 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收。
根部已经访问不到,所以自动清除。
总结:标记清除法的核心思路就是, 从根部扫描对象,能查找到的就是使用的,查找不到的就要回收。
4.4.2 什么是闭包
闭包的概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域。
(看完晦涩难懂的概念后是什么感觉呢? 是不是一脸懵,和没看一样?没关系,这才是正常的反应)
其实简单的理解就是: 闭包 = 内层函数 + 外层函数的变量
先来看一段简单的代码:
function outer(){
const a = 1
function f(){
console.log(a)
}
f()
}
outer()
从上述代码中我们可以看到,外层函数中有个变量a,同时嵌套一个内层函数 f(), 在内层函数中用到了外层函数的变量a, 所以函数 f() 和 变量 a 就构成了闭包。没错,就是这么简单。
4.4.2 闭包的作用是什么?
简单的说,闭包的作用就是:封装数据,提供操作,使外部也可以访问函数内部的变量。(就拿上述的代码来做个说明。 在正常情况下,outer() 函数内部的变量a 是不能被外面获取到的,而要想外部可以访问outer() 函数中的变量,就要使用闭包来完成。)
//常见的闭包的形式:外部可以访问使用函数内部的变量
function outer(){
let a = 10
function fn(){
console.log(a)
}
return fn
}
//outer() === fn === function fn(){}
//const fun = funciton fn(){}
const fun = outer()
fun() //调用函数
//外面要用到这个10
在上述代码段中,由于函数outer()的返回值是fn,也就是内部函数,所以在调用outer()时得到的其实就是fn,也就是 function fn(){ } 。 那么此时我定义一个fun常量,并把outer()的调用结果赋给fun,那么在使用fun时就用到了outer() 函数内部的变量a 。
4.4.3 闭包的应用
应用:实现数据的私有
eg:我们要做个统计函数调用次数的程序,函数调用一次,就++。
4.5 箭头函数
4.5.1 基本语法
1. 箭头函数的基本语法如下所示:
//普通函数
const fn = function(){
console.log(123)
}
//箭头函数
const fn = () => {
console.log(123)
}
fn()
可以看到,所谓的箭头函数的直观写法就是:箭头(=>)代替关键字function
2. 只有一个形参的时候,可以省略小括号
const fn = x => {
console.log(x)
}
fn()
注:只有一个形参的时候可以省,其他情况( 没有形参、两个及以上都不能省 )。没有形参的时候如果省了就只剩下:==>了,这就会给读者带来疑惑。
3. 只有一行代码的时候, 可以省略大括号
const fn = x => console.log(x)
fn(1)
4. 只有一行代码的时候,可以省略return
const fn = x => x + x
console.log(fn(1))
5. 箭头函数可以直接返回一个对象(返回对象时要用小括号包裹)
const fn = (uname) => ( {uname: uname} )
fn('郭凯旋')
只是因为对象本身就是由{......}包裹的属性名和属性值对构成的,所以为了和函数的{ }区分,在返回对象的时候用小括号来包裹。
总结:
- 箭头函数属于表达式函数,因此不存在函数提升。
- 箭头函数只有一个参数时可以省略圆括号()
- 箭头函数函数体只有一行代码时可以省略花括号{}, 并自动作为返回值被返回。
- 加括号的函数体返回对象字面量表达式。
4.5.2 箭头函数参数
- 普通函数有arguments动态参数
- 箭头函数没有arguments动态参数, 但是有剩余参数...args
//1.利用箭头函数来求和
const getSum = (...arr) => {
let sum = 0
for(let i = 0; i < arr.length; i++){
sum += arr[i]
}
return sum
}
const result = getSum(2, 3)
console.log(result)
4.5.3 箭头函数this
以前的函数的this指向
//以前的this指向: 谁调用了这个函数, this 就指向谁
console.log(this) //window
//普通函数
function fn(){
console.log(this) //window
}
window.fn()
//对象方法里面的this
const obj = {
name: 'triumph',
sayHi: function (){
console.log(this) //obj
}
}
箭头函数this
在箭头函数出来之前, 每一个新函数根据它是被如何调用的来定义这个函数的this值,是非常令人讨厌的。
箭头函数不会创建自己的this, 它只会从自己的作用域的上一层沿用this。
console.log(this) //此处为window
const sayHi = function() {
console.log(this) //普通函数指向的是调用者,这里指向的是window
}
btn.addEventListener('click', function () {
console.log(this) //当前this 指向btn,因为是btn调用的
})
再来看一个例子
const user = {
name: '小明',
sleep: function() {
console.log(this) //指向user
const fn = () => {
console.log(this) //指向user, 该箭头函数中的this与sleep中的this一致
}
//调用箭头函数
fn()
}
}
user.sleep()
对象方法箭头函数this
//对象方法箭头函数this
const obj = {
uname: '凯旋',
sayHi: () => {
console.log(this) //this指向谁? window
}
}
obj.sayHi()
cosnt obj = {
uname: 'kaixuan',
sayHi: function () {
console.log(this)
let i = 10
const count = () => {
console.log(this) //obj 因为箭头函数的this是去上一层作用域找,而上层函数的this指向的是函数的调用者,也就是obj
}
count()
}
}
obj.sayHi()
总结
- 箭头函数不会创建自己的this, 它只会从自己的作用域链的上一层沿用this
- DOM事件回调函数推荐使用箭头函数吗?
不太推荐,特别是需要用到this的时候
事件回调函数使用箭头函数时,this为全局的window。
4.6 原型链继承
4.6.1 构造函数
- 封装是面向对象思想中比较重要的一部分, js面向对象可以通过构造函数实现封装。
- 同样的将变量和函数组合到一起并能通过this实现数据的共享,所不同的是借助构造函数创建出来的实例对象之间是彼此不影响的。
- 这种方式创建对象可以获得构造函数中封装的所有逻辑,但是存在浪费内存的问题。
<script>
function Star(uname, age){
this.uname = uname;
this.age = age;
this.sing = function(){
console.log('I can sing');
}
}
const kaixuan = new Star('kaixuan', 25)
const dabing = new Star('dabing', 23)
console.log(kaixuan.sing == dabing.sing) //结果是false,说明俩函数不一样
</script>
4.6.2 原型
什么是原型?
在js中,每个构造函数内部都有一个prototype属性,该属性的值是个对象,该对象包含了该构造函数所有实例共享的属性和方法。当我们通过构造函数创建对象的时候,在这个对象中有一个指针,这个指针指向构造函数的prototype的值,我们将这个指向prototype的指针称为原型。
<script>
function Star(uname, age){
this.uname = uname;
this.age = age;
this.sing = function(){
console.log('唱歌')
}
}
const kaixuan = new Star('凯旋', 25)
const dabing = new Star('黄冰', 23)
console.log(kaixuan === dabing)
console.log(kaixuan.sing == dabing.sing)
</script>
在上述代码块中,两个对象的sing()方法是一模一样的,但是在实际中却是开辟了两块空间。所以应该把公共的属性写到构造函数里面,公共的方法写到原型对象身上。
function Star(uname, age){
this.uname = uname;
this.age = age;
// this.sing = function(){
// console.log('唱歌')
// }
}
//公共的方法写到原型对象身上
Star.prototype.sing = function(){
console.log('唱歌')
}
const kaixuan = new Star('凯旋', 25)
const dabing = new Star('黄冰', 23)
kaixuan.sing(); //调用
dabing.sing(); //调用
console.log(kaixuan === dabing)
console.log(kaixuan.sing === dabing.sing) //结果是true, 说明俩函数一样,共享
4.6.3 原型继承
继承是面向对象编程的一大特征,通过继承进一步提升代码封装的程度,Javascript中大多数是借助原型对象实现继承的特性。
继承的实现:类似与java当中的父子类
比如如下的例子:
<script>
function Woman(){
this.eyes = 2
this.head = 1
}
const kaixuan = new Woman()
console.log(kaixuan)
function Man(){
this.eyes = 2
this.head = 1
}
const dabing = new Man()
console.log(dabing)
</script>
在上述代码块中,Woman和Man对象有着共同的属性,所以为了简化代码的编写,就可以把这些共同的属性抽离出来。
<script>
//继续抽取: 公共的部分放到原型上
const Person = {
eyes: 2,
head: 1
}
//女: 构造函数 要继承Person
function Woman(){
}
//Woman 通过原型来继承Person
Woman.prototype = Person
const kaixuan = new Woman()
console.log(kaixuan)
//男 : 构造函数 要继承Person
function Man(){
}
const dabing = new Man()
console.log(dabing)
</script>
5.内部对象
标准对象
typeof 123
'number'
typeof '123'
'string'
typeof true
'boolean'
typeof NaN
'number'
typeof []
'object'
typeof{}
'object'
typeof undefined
'undefined'
typeof Math.abs
'function'
typeof null
'object'
5.1 Date
var now = new Date(); //Tue Sep 26 2023 11:49:43 GMT+0800 (中国标准时间)
now.getFullYear(); //年
now.getMonth(); //月 0 ~ 11
now.getDate(); //日
now.getDay(); //星期几
now.getHours(); //时
now.getMinutes(); //分
now.getSeconds(); //秒
now.getTime(); //时间戳 全世界统一1970年1月1日开始的毫秒数
console.log(new Date(184547593843)) //时间戳转换为时间
运行结果如下:
时间格式
now = new Date(1578106175991)
Sat Jan 04 2020 10:49:35 GMT+0800 (中国标准时间)
now.toLocaleString()
'2020/1/4 10:49:35'
now.toGMTString()
'Sat, 04 Jan 2020 02:49:35 GMT'
5.2 JSON
JSON是什么
早期, 所有的数据传输习惯使用XML文件!
- JSON(JavaScript Object Notation, JS对象简谱)是一种轻量级的数据交换格式。
- 简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。
- 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
在JavaScript中一切皆为对象、任何js支持的类型都可以用JSON来表示;
格式:
- 对象都用{}
- 数组都用[]
- 所有的键值对都使用key : value
JS对象和JSON 格式区别
注:json格式的key和value都是字符串, 且json本身就是个字符串
JSON字符串和JS对象的转化
var user = {
name: "kaixuan",
age : 25,
sex : "女"
}
//对象转化为json字符串
var jsonUser = JSON.stringify(user);
//json字符串转换为对象(参数为json字符串)
var obj = JSON.parse('{"name":"kaixuan","age":25,"sex":"女"}');
5.3 Ajax
- 原生的js写法, xhr异步请求
- jQuery 封装好的方法 $("#name").ajax("")
- axios 请求
6. 面向对象编程
6.1 什么是面向对象编程
原型对象
javascript、 java、 C# .....面向对象; javascript有些区别!
- 类: 模板
- 对象: 具体的实例
在Javascript中要换一下思维方式!
原型
var Student = {
name : "kaixuan",
age: 25,
run: function () {
console.log(this.name + " run...");
}
};
var dabing = {
name:"dabing"
};
//大冰的原型是 Student (注:这里的__是两个下划线)
dabing.__proto__= Student;
var Bird = {
fly: function () {
console.log(this.name + " fly...")
}
};
dabing.__proto__= Bird;
class继承
class关键字,是在ES6引入的
1. 定义一个类,属性,方法
//定义一个学生类
class Student{
constructor(name){
this.name = name;
}
hello(){
alert('hello');
}
}
var dabing = new Student("dabing");
var kaixuan = new Student("kaixuan");
dabing.hello();
kaixuan.hello();
2. 继承
class Student{
constructor(name){
this.name = name;
}
hello(){
alert('hello');
}
}
var dabing = new Student("dabing");
var kaixuan = new Student("kaixuan");
class Pupil extends Student{
constructor(name, grade){
super(name);
this.grage = grade;
}
myGrade(){
alert("I'm a pupil");
}
}
var runze = new Pupil("runze");
本质: 查看对象原型
原型链
__proto__:
7.操作BOM对象
浏览器介绍
JavaScript 和 浏览器 的关系?
JavaScript诞生就是为了能够让它在浏览器中运行!
BOM: 浏览器对象模型
- IE 6 ~ 11
- Chrome
- Safari
- FireFox
- Opera
三方浏览器
- QQ浏览器
- 360浏览器
window对象
window代表浏览器窗口
window.alert(1)
undefined
window.innerHeight
79
window.innerWidth
1038
window.outerHeight
79
window.outerWidth
1037
Navigator
Navigator, 封装了浏览器的信息。
navigator.appName
'Netscape'
navigator.appVersion
'5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36'
navigator.userAgent
'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36'
navigator.platform
'Win32'
大多数时候,我们不会使用navigator对象,因为会被人为修改!
不建议使用这些属性来判断和编写代码。
screen
代表屏幕的尺寸
screen.width
1037
screen.height
300
location (重要)
location代表当前页面的URL信息
host:"www.baidu.com"
href:"https://www.baidu.com/"
protocol:"https:"
reload:f reload() //刷新网页
location.assign('https://www.youtube.com/watch?v=-UaZT7tajxI')
document
document 代表当前的页面, HTML DOM文档树
document.title //获取
'百度一下'
document.title='凯旋' //修改
'凯旋'
获取具体的文档树节点
<dl id = "app">
<dt>Java</dt>
<dd>JavaSE</dd>
<dd>JavaEE</dd>
</dl>
<script>
var dl = document.getElementById("app");
</script>
获取cookie
劫持cookie原理
www.taobao.com
<script src = "aa.js"></script>
<!--恶意人员; 获取你的cookie上传到它的服务器 -->
服务器端可以设置cookie: httpOnly
(如果cookie中设置了HttpOnly属性,那么通过js脚本将无法读取到cookie信息,这样能有效的防止XSS攻击,窃取cookie内容,这样就增加了cookie的安全性,即便是这样,也不要将重要信息存入cookie。XSS全称Cross SiteScript,跨站脚本攻击,是Web程序中常见的漏洞,XSS属于被动式且用于客户端的攻击方式,所以容易被忽略其危害性。其原理是攻击者向有XSS漏洞的网站中输入(传入)恶意的HTML代码,当其它用户浏览该网站时,这段HTML代码会自动执行,从而达到攻击的目的。如,盗取用户Cookie、破坏页面结构、重定向到其它网站等。)
history
history代表浏览器的历史记录
history.back() //后退
history.forward() //前进
8. 操作BOM对象(重点)
DOM: 文档对象模型
浏览器网页就是一个Dom树形结构
- 更新:更新Dom节点
- 遍历dom节点: 得到Dom节点
- 删除:删除一个Dom节点
- 添加:添加一个新的节点
要操作一个Dom节点, 就必须要先获得这个Dom节点
获得Dom节点
<div id="father">
<h1>标题一</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
</div>
<script>
//对应css选择器
var h1 = document.getElementsByTagName('h1');
var p1 = document.getElementById('p1');
var p2 = document.getElementsByClassName('p2');
var father = document.getElementById('father');
var children = father.children; //获取父节点下的所有子节点
</script>
这是原生代码,之后我们尽量都使用jQuery();
更新节点
<div id="id1">
</div>
<script>
var id1 = document.getElementById('id1');
</script>
操作文本
- id1.innerText='凯旋' : 修改文本的值
- id1.innerHTML='<strong>123</strong>' : 可以解析HTML文本
操作CSS
id1.style.color = 'yellow'; //属性使用 字符串 包裹
id1.style.fontsize='20px'; // -转 驼峰命名问题
id1.style.padding='2em';
删除节点
删除节点的步骤:先获取父节点,再通过父节点删除自己
<div id="father">
<h1>标题一</h1>
<p id="p1">p1</p>
<p class="p2">p2</p>
</div>
<script>
var p1 = document.getElementById('p1');
var father = p1.parentElement;
father.removeChild(p1);
//删除是一个动态的过程
father.removeChild(father.childern[0])
father.removeChild(father.childern[1])
father.removeChild(father.childern[2])
</script>
注意:删除多个节点的时候,children是在时刻变化的(在删除掉了一个节点后,其余节点的下标索引是会变化的), 删除节点的时候一定要注意!
插入节点
我们获得了某个Dom节点,假设这个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>
var js = document.getElementById('js');
var list = document.getElementById('list');
list.appendChild(js); //追加到后面
</script>
创建一个新的标签,实现插入
<script>
var js = document.getElementById('js');
var list = document.getElementById('list');
//通过js创建一个新的节点
var newP = document.createElement('p'); //创建一个p标签
newP.id = 'newP';
newP.innerText = 'Hello,kaixuan';
list.appendChild(newP);
</script>
9. 操作表单
表单是什么 form DOM树
- 文本框 text
- 下拉框 <select>
- 单选框 radio
- 多选框 checkbox
- 隐藏域 hidden
- 密码框 password
- ..........
表单的目的: 提交信息
获得要提交的信息
<form action="post">
<p>
<span>用户名:</span> <input type="text" id="username">
</p>
<p>
<span>性别: </span>
<input type="radio" name="sex" value="man" id="boy">男
<input type="radio" name="sex" value="woman" id="girl">女
</p>
</form>
<script>
//获取到输入框这个节点
var input_text = document.getElementById('username');
var boy_radio = document.getElementById('boy');
var girl_radio = document.getElementById('girl');
//得到输入框的值
input_text.value
//修改输入框的值
input_text.value='123'
//查看返回的结果是否为true,来判断当前节点是否被选中,如果为true,表示被选中
boy_radio.checked;
//赋值(让某一个节点成为被选中的状态)
boy_radio.checked = true;
</script>
提交表单。 md5加密密码, 表单优化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--MD5-->
<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js"></script>
</head>
<body>
<!--
表单绑定提交事件
onsubmit= 绑定一个提交检测的函数, true, false
将这个结果返回给表单, 使用onsubmit接收!
onsubmit="return aa()"
-->
<form action="#" method="post" onsubmit="return aa()">
<p>
<span>用户名: </span> <input type="text" id="username" name="username">
</p>
<p>
<span>密码:</span> <input type="password" id="input-password">
</p>
<input type="hidden" id="md5-password" name="password">
<!--绑定事件 onclick 被点击-->
<button type="submit" onclick="aa()">提交</button>
</form>
<script>
function aa(){
var uname = document.getElementById('username');
var pwd = document.getElementById('input-password');
var md5pwd = document.getElementById('md5-password');
md5pwd.value=md5(pwd.value);
//可以检验判断表单内容,true就是通过提交,false:阻止提交
return true;
}
</script>
</body>
</html>
10.jQuery
jQuery库,里面存有大量的JavaScript函数
获取jQuery
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--cdn引入-->
<script src="https://cdn.bootcss.com/jquery/3.4.1/core.js"></script>
</head>
<body>
</body>
</html>
jQuery的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
</head>
<body>
<button id="btn">点击弹框</button>
</body>
<script>
//选择器就是css的选择器
$("#btn").click(function() {
alert('hello jQuery')
})
</script>
</html>
选择器
<script>
//标签选择器
document.getElementsByTagName();
//id选择器
document.getElementById();
//类选择器
document.getElementsByClassName();
//jQuery css 中的选择器它全部都能用!
$('p').click(); //标签选择器
$('#id1').click(); //id选择器
$('.class1').click() //class选择器
</script>
事件
鼠标事件、键盘事件、其他事件
项目 | Value |
click | 单击鼠标左键时发生,如果右键也按下则不会发生。当用户的焦点在按钮上并按了 Enter 键时,同样会触发这个事件 |
dblclick | 双击鼠标左键时发生,如果右键也按下则不会发生 |
mousedown | 单击任意一个鼠标按钮时发生 |
mouseout | 鼠标指针位于某个元素上且将要移出元素的边界时发生 |
mouseover | 鼠标指针移出某个元素到另一个元素上时发生 |
mouseup | 松开任意一个鼠标按钮时发生 |
mousemove | 鼠标在某个元素上时 持续 发生 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<style>
#divMove{
width: 500px;
height:500px;
border: 1px solid red;
}
</style>
</head>
<body>
<!--要求:获取鼠标当前的一个坐标-->
mouse: <span id="mouseMove"></span>
<div id="divMove">
在这里移动鼠标试试
</div>
<script>
//当网页元素加载完毕之后,响应事件
// $(document).ready(function(){
//
// })
//简写形式
$(function(){
$('#divMove').mousemove(function (e) {
$('#mouseMove').text('x:' + e.pageX + 'y: ' + e.pageY);
})
});
</script>
</body>
</html>
操作Dom
节点文本操作
$('#test-ul li[name=python]').text(); //获得值
$('#test-ul li[name=python]').text('凯旋'); //设置值
$('#test-ul').html(); //获得值
$('#test-ul').html('<strong>123</strong>'); //设置值
css的操作
$('#test-ul li[name=python]').css("color","red");
元素的显示和隐藏: 本质display : none
$('#test-ul li[name=python]').show()
$('#test-ul li[name=python]').hide()
测试娱乐
小技巧
- 如何巩固JS (看jQuery源码, 看游戏源码! )
- 巩固html和css (扒网站, 或者是全部download下来,然后对应修改看效果 )