一.JavaScript语法阶段
1.引言
JavaScript是一种运行在客户端的脚本语言
脚本语言:不需要编译,运行过程中有js解释器(js引擎)逐行来进行解释并执行
现在也可以基于Node.js技术进行服务器端编程
作用:
(1)表单动态检验(密码强度检测)
(2)网页特效
(3)服务端开发(Node.js)
(4)桌面程序(Electron)
(5)App
(6)物联网
(7)游戏开发(cocos2d-js)
浏览器分为渲染引擎和JS引擎两部分
渲染引擎:用来解析HTML和CSS,俗称内核
JS引擎:用来读取网页中的JavaScript代码,对其处理后运行,比如chrome的V8。JS引擎执行JS语句是一行一行的执行的。
组成: ECMAScript(JavaScript语法)、DOM(页面文档对象模型)、BOM(浏览器对象模型)
JS三种形式:
1.行内式:
<input type="button" value="点我试试" onclick="alert('Hello world')" />
2.内嵌JS:
<script>
alert("Hello world!");
</script>
3.外部JS文件
<script src="XXXX"></script>
引用外部JS文件的script标签中间不可以写代码
使用JS代码量比较大的情况
使用!+Tab键可以快速生成html文件的基本内容
JS注释
// 1.单行注释 ctrl+/
/* 2.多行注释 默认快捷键 shift+alt+a
2.多行注释 vscode中修改多行注释的快捷键: ctrl+shift+/
*/
JavaScript输入输出语句
<script>
//浏览器弹出警示框
alert("AAA");
//浏览器控制台打印输出信息
console.log("BBB");
//浏览器弹出输入框,提供给用户输入,取过来的值是字符型的
prompt("请输入你的密码:");
</script>
2.变量
2.1变量初始化
<script>
//变量声明+赋值=变量初始化
var age = 10;
//多个变量用逗号隔开
var age = 13,gender = '男';
//只声明不赋值的结果是undifined
</script>
2.2数据类型
JS是一种弱类型或者说动态语言
js的变量数据类型是只有程序在运行过程中,根据等号右边的值来确定的
JS的简单数据类型有:
Number :整数、浮点数、0b10001 034 34 0x34
Number.MAX_VALUE(最大值) Number.MIN_VALUE(最小值)
Infinity(无穷大)、-Infinity(无穷小)
isNaN() 判断非数字,是数字返回false,不是数字返回true
String :推荐使用单引号,外双内单或者外单内双
var str = ‘Hello World!’;
console.log(str.length);
字符串相加与java类似
console.log(“hello world”+“!”);
字符串的不可变:指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中开辟了一个内存空间
Boolean(false、true):默认是false
Undefined Null
获取变量数据类型:
var gender = '男';
alert(typeof gender);
数据类型转化:
1.转换为string
var num = 10;
alert(num.toString());
alert(String(num));
//隐式转换
alert(num+‘’);
2.string转换为number
var str = ‘23’;
alert(parseInt(str));
parseFloat
3.转换为布尔型
Boolean(var);
复杂数据类型有:Object
解释型语言和编译型语言
编译器是在代码执行之前进行编译,生成中间代码文件
解释器是在运行时进行及时解释,并立即执行
3.运算符
一共有算术运算符、递增和递减运算符、比较运算符、逻辑运算符、赋值运算符等
alert(3/2); //结果为1.5,而不是1
浮点依旧还是有误差
===、!== 要求值和数据类型都一致
短路运算和其他语言一样
4.流程控制
顺序结构、分支结构、循环结构
if-else if-else语句、三元表达式、switch-case语句与java类似
case里面匹配时是===全等
断点调试:
for循环、while循环等与java也类似
5.数组
//js里的数组元素可以是不同数据类型的
var num = [];
num[1] = 23;
num[2] = 'sdfds';
for(var i=0;i<num.length;i++){
console.log(num[i]);
}
//js里的数组长度可以进行修改
1.新增数组元素,修改length长度
var num = [];
num[1] = 23;
num[2] = 'sdfds';
console.log(num.length);
num.length = 100;
2.新增数组元素,修改索引号,追加数组元素
var color = ['red','green','blue'];
arr[3] = 'pink';
6.函数
函数封装了一些被大量重复使用的代码
function getSum(num1,num2){
var sum = 0;
for(var i=num1;i<=num2;i++){
sum += i;
}
return sum;
}
var num = getSum(1,100);
//函数没有return时则返回undefined
arguments:
function fn(){
console.log(arguments); //里面存储了所有传递过来的实参
}
fn(1,2,3);
//arguments是一个伪数组,并不是真正意义上的数组
//1.具有数组的length属性
//2.按照索引的方式进行存储的
//3.它没有真正数组的一些方法,如pop() push()等
函数声明的两种方式:
//1.利用函数关键字自定义函数
function fn(){
console.log(arguments);
}
fn(1,2,3);
//2.函数表达式(匿名函数)
var 变量名 = function(){};
var fun = function(){
console.log('我是函数表达式'):
}
fun();
7.作用域
全局变量:在全局作用域下的变量、或者在函数内部没有声明直接赋值的变量
从执行效率来看全局变量和局部变量:
1.全局变量只有浏览器关闭的时候才会销毁,比较占内存资源
2.局部变量当我们程序执行完毕就会销毁,比较节约内存资源
JS在es6的时候才新增了块级作用域,也就是说在之前下列代码是合理的:
if(3<5){
var num = 3;
}
console.log(num);
作用域链: 内部函数访问外部函数的变量,采取的是链式查找的方式来决定取哪个值,这种结构我们称为作用域链就近原则
js中的var与c++中的var区别(个人理解)
(1)如果都是函数内部定义的局部变量,自然条件下外部都不能访问
(2)外部定义了全局变量,js函数内不需要通过形参传递就可以使用,而c++需要传递进来才能使用
window是一个全局对象,定义在全局作用域中的变量、函数都会变成window对象的属性和方法,这也使得js函数内可以不经参数传递就直接使用全局变量
8.预解析
1.js引擎运行js分为两步: 预解析 代码执行
(1)预解析 js引擎会把js里面所有的var还有function提升到当前作用域的最前面
(2)代码执行 按照代码书写的顺序从上往下执行
2.预解析分为变量预解析(变量提升)和函数预解析(函数提升)
(1)变量提升就是把所有的变量声明提升到当前的作用域最前面,不提升赋值操作
(2)函数提升就是把所有的函数声明提升到当前作用域的最前面,不调用函数
console.log(num);
var num = 10;
//这两句代码在预解析时相当于下述代码:
var num; //注意:变量提升只针对声明而不针对赋值
console.log(num);
var num = 10;
//所以最后的结果是undifined
//函数声明和函数表达式的执行顺序不同,函数声明会被提升,函数表达式不会提升
//函数表达式不会被提升,而是在代码执行到它们所在的行时进行赋值
fun();
var fun = function(){
console.log(22);
}
预解析相当于:
var fun;
fun(); //这行报错
var fun = function(){
console.log(22);
}
//JavaScript 引擎会先将函数声明提升到代码的顶部,然后再执行函数调用
fn();
function fn(){
console.log(11);
}
//这相当于执行:
function fn(){
console.log(11);
}
fn();
案例1
var num = 10;
fun();
function fun(){
console.log(num);
var num = 20;
}
相当于执行以下操作:
var num;
// function里面的也要进行变量提升
function fun(){
var num; //注意这两处
console.log(num);
num = 20; //注意这两处
}
num = 10;
fun();
//所以最终结果为undifined
案例2
f1();
console.log(c);
console.log(b);
console.log(a); //这行报错,其余都为9
function f1(){
var a=b=c=9; //这相当于var a=9;b=9;c=9; 所以b和c是全局变量
console.log(a); //集体声明应该为 var a=9,b=9,c=9;
console.log(b);
console.log(c);
}
9.对象
创建对象的第一种方式
//1.利用对象字面量创建对象
var obj = {
name: '张三丰',
age: 18,
sex: '男',
sayHi: function(){
console.log('Hello World!');
}
}
//(1)里面的属性或者方法我们采用键值对的形式
//(2)多个属性和方法之间使用逗号隔开
//(3)方法冒号后面跟的是一个匿名函数
2.使用对象
//(1)使用对象名.属性名的方式调用对象的属性
//(2)使用对象名['方法名']的方式调用对象的属性
//(3)使用对象名.方法名调用对象的方法
创建对象的第二种方式
//使用new Object创建对象
var obj = new Object();
obj.name = '张三丰';
obj.age = 18;
obj.sex = '男';
obj.sayHi = function(){
console.log('hi~');
}
创建对象的第三种方式
//使用构造函数创建对象
//前两种创建对象的方式一次只能创建一个对象
//构造函数的语法格式
function 构造函数名(){
this.属性 = 值;
this.方法 = function(){}
}
new 构造函数名();
//构造函数内部一定要使用this
function Star(name,age,sex){
this.name= name;
this.age = age;
this.sex = sex;
this.sing = function(sang){
console.log(sang);
}
}
var ldh = new Star('刘德华',18,'男');
ldh.sing('冰雨');
//new关键字的执行过程:
(1)new构造函数可以在内存中创建一个空的对象
(2)this就会指向刚才创建的空对象
(3)执行构造函数里面的代码,给这个空对象添加属性和方法
(4)返回这个对象
//遍历对象
//对于ldh对象来说:
for(var k in ldh){
console.log(k); //得到的是属性名
console.log(ldh[k]); //得到的是属性值
}
10.内置对象
js中的对象分为三种:自定义对象、内置对象、浏览器对象
内置对象是js自带的一些对象,最大的优点是帮助我们开发
例如:Math、Date、Array、String等;
MDN文档官网
10.1 Math对象
//绝对值
Math.abs(-1);
//上取整
Math.ceil(1.1);
//下取整
Math.floor(1.1);
//四舍五入 .5会往大了取
Math.round(1.4);
//随机数方法Math.random()返回[0,1)之间的一个浮点数
//获得[min,max)之间的随机整数
Math.floor(Math.random()*(max-min))+min
//获得[min,max]之间的随机整数
Math.floor(Math.random()*(max-min+1))+min
10.2 Date对象
//没有参数返回当前系统的当前时间
var date = new Date();
console.log(date);
var date1 = new Date('2024-3-8 12:33:12');
console.log(date1);
//按照需要的格式输出当前日期
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth()+1; //这里需要+1
var dates = date.getDate();
var day = date.getDay();
var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六']
var hour = date.getHours();
var minute = date.getMinutes();
var second = date.getSeconds();
console.log('今天是'+year+'年'+month+'月'+dates+'日'+arr[day]+hour+':'+minute+':'+second);
10.3 数组对象
//数组创建的两种方式
//1.用字面量来创建
var arr = [1,2,3];
//2.用new Array()
var arr1 = new Array();
var arr2 = new Array(2);
var arr3 = new Array(2,3);
//检测是否为数组
//第一种:使用instanceof
var arr = [];
console.log(arr instanceof Array);
//第二种:使用isArray
Array.isArray(arr);
//数组元素的添加和删除
var arr = [1,2,3];
arr.push(4); //返回的是数组长度
arr.unshift(5); //在开头添加
console.log(arr);
arr.pop(); //返回的是删除的元素的值
arr.shift(); //在开头删除
console.log(arr);
//数组排序
sort函数接受一个可选的比较函数作为参数,当比较函数返回一个负值时,表示第一个参数应该排在第二个参数之前;当返回一个正值时,表示第一个参数应该排在第二个参数之后;当返回0时,表示两个参数相等,顺序不变
//降序
var arr = [2,43,3,2,23,5];
arr.sort(function(a,b){
return b-a;
});
console.log(arr);
//升序
var arr = [2,43,3,2,23,5];
arr.sort(function(a,b){
return a-b;
});
console.log(arr);
//返回数组元素的下标,不存在则返回-1
var arr = [23,3,5];
console.log(arr.indexOf(3));
//数组转换为字符串
//使用toString
var arr = [23,4,34];
console.log(arr.toString());
//使用join,join里面为分隔符
console.log(arr.join('-'));
console.log(arr.join(''));
10.4字符串对象
//1.替换字符 replace('被替换字符','替换字符')
var str = 'anady';
//replace只替换第一个
console.log(str.replace('a','b'));
//replaceAll替换全部的
console.log(str.replaceAll('a','b'));
//2.字符串转化为数组
var str = 'red,pink,black';
var arr = str.split(',');
console.log(arr);
11.数据类型
简单数据类型传参:
当把一个值类型变量作为参数传递给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,方法中修改不会影响到外部变量。
复杂数据类型传参:
在 JavaScript 中,复杂数据类型(例如对象和数组)作为参数传递给函数时,实际上传递的是引用而不是值的副本。这意味着,如果在函数内部修改了传递的对象或数组,那么外部的原始对象或数组也会受到影响。
但是需要注意的是:
(1)你可以在函数里面修改参数对象的属性(在外部也生效),但是不可以直接对这个参数对象整体赋值(或者说在函数内部将参数重新赋值为一个新的对象或数组),这种整体赋值的操作在外部是不会生效的。
<script>
function modifyObject(obj) {
obj.key = 'modified';
}
let myObject = {
key: 'original'
};
modifyObject(myObject);
console.log(myObject); // 输出:{ key: 'modified' }
</script>
(2)如果传递参数的属性也是一个对象,则可以整体修改这个属性对象,外部也会生效。
<script>
function modifyNestedObject(obj) {
obj.innerObject = {
key: 'new value'
};
}
let myObject = {
innerObject: {
key: 'original value'
}
};
modifyNestedObject(myObject);
console.log(myObject); // 输出:{ innerObject: { key: 'new value' } }
</script>
二.Web APIs
Web APIs中主要学习DOM和BOM,是JS独有的部分
Web APIs是浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)
MDN详细API
1.DOM
DOM(Document Object Model)文档对象模型,是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。
通过这些DOM接口可以改变网页的内容、结构和样式
1.1获取元素
1.1.1第一种方式:getElementById();
//自上到下加载,script写到标签下面
<div id="time">2024-3-8</divid>
<script>
var timer = document.getElementById('time');
console.dir(timer);
</script>
1.1.2 第二种方式:getElementsByTayName();
可以获取某个父元素内部所有指定标签名的子元素。注意:父元素必须是单个对象(必须指明是哪一个元素对象)
<ul>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
<ol id = 'ol'>
<li>AAA</li>
<li>BBB</li>
<li>CCC</li>
<li>DDD</li>
</ol>
//第一种写法
<script>
var ol_tag = document.getElementsByTagName('ol');
//返回的是获取过来元素对象的集合,以伪数组的形式存储
var ol_li = ol_tag[0].getElementsByTagName('li'); //注意:这里要用ol_tag[0],不能直接用ol_tag
</script>
//第二种写法
<script>
var ol_id = document.getElementById('ol');
//返回的是获取过来元素对象的集合,以伪数组的形式存储
var ol_li = ol_id.getElementsByTagName('li');
</script>
1.1.3 第三种方式:HTML5新增方式
<div class="box">盒子</div>
<div class="box">盒子</div>
<div id="nav">
<ul>
<li>首页</li>
</ul>
</div>
<script>
//1.根据类名获得某些元素的集合
var boxs = document.getElementsByClassName('box');
console.log(boxs);
//2.querySelector 返回指定选择器的第一个元素对象
//querySelector里面的选择器需要加符号 .box #nav
var firstBox = document.querySelector('.box');
console.log(firstBox);
var firstNav = document.querySelector('#nav');
console.log(firstNav);
var li = document.querySelector('li');
console.log(li);
//3.querySelectorAll返回指定选择器的所有元素对象集合
var allBox = document.querySelectorAll('.box');
</script>
1.1.4第四种方式:获取特殊元素
获取body、html对象
<script>
//1.获取body元素
var body = document.body;
//2.获取html元素
var htmlEle = document.documentElement;
</script>
1.2.事件基础
JS使我们有能力创建动态界面,而事件是可以被js侦测到的行为
简单理解:触发—响应机制
1.2.1事件三要素
事件由事件源、事件类型、事件处理程序三部分组成
(1)事件源:事件被触发的对象 btn
<button id="btn">hk</button>
<script>
var btn = document.getElementById('btn');
</script>
(2)事件类型:如何触发 什么事件 比如鼠标点击还是鼠标经过、键盘按下等
使用onclick和addEventListener()的最主要的区别在于onclick绑定了一个事件就不能再绑定,而addEventListener()可以绑定多个
(3)事件处理程序:通过一个函数赋值的方式完成
<button id="btn">hk</button>
<script>
var btn = document.getElementById('btn');
btn.onclick = function(){ //onclick就是时间类型
alert('哈哈');
}
</script>
1.2.2执行事件的步骤
(1)获取事件源
(2)绑定事件(也叫注册事件)
(3)添加事件处理程序
1.2.3操作元素
1.2.3.1.修改元素内容:innerText和innerHTML
<button id="btn">点击显示当前时间</button>
<div>某个时间</div>
<script>
var btn = document.getElementById('btn');
var div = document.querySelector('div');
btn.onclick = function(){
div.innerText = getDate();
}
function getDate(){
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth()+1; //这里需要+1
var dates = date.getDate();
var day = date.getDay();
var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六']
var hour = date.getHours();
var minute = date.getMinutes();
var second = date.getSeconds();
return '今天是'+year+'年'+month+'月'+dates+'日'+arr[day]+" "+hour+':'+minute+':'+second;
}
</script>
innerText和innerHTML的区别
//innerText不识别html标签,innerHTML识别,W3C建议使用后者
//innerText去除空格和换行,innerHTML保留空格和换行
1.2.3.2.修改常见元素属性
以src为例,href等其他属性同理
<img src = "XXXX" alt="" title="XX">
<script>
var img = document.querySelector('img');
img.src = "YYYY";
img.title = "YY";
</script>
1.2.3.3.修改表单元素属性
//表单里的值是通过value得到的
<button>按钮</button>
<input type="text" value="输入内容">
<script>
var btn = document.querySelector('button');
var input = document.querySelector('input');
btn.onclick = function(){
input.value = '被点击了';
//如果想实现按钮点击后被禁用
btn.disabled = true;
//this.disabled = true; 这行会更好
}
</script>
1.2.3.4样式属性操作
1.2.3.4.1通过行内样式操作进行样式属性修改
(1)获取对象
(2)使用 对象.style.属性=XXX;
注意: 1.js里面的样式采取驼峰命名法,比如fontSize
2.js修改style样式操作,产生的是行内样式,CSS权重比较高
3.这种方式适合要修改的样式比较少,或者功能简单的情况
//onfocus和onblur案例
<input type="text" value="手机">
<script>
var input = document.querySelector('input');
input.style.color = "#999";
input.onfocus = function(){
if(this.value == '手机'){
this.value = null;
}
this.style.color = '#333';
}
input.onblur = function(){
if(this.value ==''){
this.value = '手机';
}
this.style.color = "#999";
}
</script>
1.2.3.4.2通过className进行样式属性修改
适用于样式较多或者功能复杂的情况
多类名选择器
<head>
<style>
div{
width:300px;
height: 200px;
background-color:aqua;
}
.change{
margin-top: 300px;
background-color: purple;
}
</style>
</head>
<body>
<div>文本</div>
<script>
var div = document.querySelector('div');
div.onclick = function(){
this.className = 'change'; //className会直接更改元素的类名,会覆盖原先的类名
//如果想要保留原先的类名,使用多类名选择器
this.className = '原先类名 change';
}
</script>
</body>
1.2.4 自定义属性值的操作
1.获取属性值:
(1)element.属性 (获取内置属性值)
(2)element.getAttribute(‘属性’); (主要获得自定义的属性)
2.设置属性值
(1)element.属性 = ‘值’
(2)element.setAttribute(‘index’,2)
///这里index是我们程序员自定义的一个属性值
<div id = "demo" index='1'></div>
<script>
var div = document.getElementById('demo');
console.log(div.getAttribute('index'));
div.setAttribute('index','2');
div.className = 'navs';
div.setAttribute('class','footer'); //注意:这里用的是'class',而不是'className'
</script>
3.H5自定义属性
自定义属性需使用getAttribute函数,而因为很难分清是自定义的属性还是内置属性,所以H5规定自定义属性data-开头作为属性名并且赋值
例如 <div data-index='1'> </div>
H5新增element.dataset.index或者element.dataset[‘index’]来获取data-index属性,但是这种方式需要再ie11之后
如果是 data-list-name,则使用驼峰命名法:element.dataset.listName
1.2.5节点操作
为什么要使用节点操作:利用DOM提供的方法获取元素时逻辑性不强且比较繁琐
利用节点层级关系获取元素:
(1)利用父子兄节点关系获取元素
(2)逻辑性强,但是兼容性差
网页中的所有内容都是节点,在DOM中,节点使用node来表示
节点至少拥有nodeType、nodeName、nodeValue这三个基本属性
(1)元素节点:nodeType为1
(2)属性节点:nodeType为2
(3)文本节点:nodeType为3(包含了文字、空格、换行等)
实际开发中,节点操作主要操作的是元素节点
节点层级常见的是父子兄层级关系
1.2.5.1父子节点
父取子: parentNode.children,可以用下标去访问
子取父:node.parentNode
<div class="demo">
<div class="box">
<span class="erweima"> </span>
</div>
</div>
<script>
var erweima = document.querySelector('.erweima');
//得到的是最近的父节点,找不到为null
var box = erweima.parentNode;
console.log(box);
//子节点 childNodes所有的子节点包含元素节点、文本节点(例如换行符)等等
//所以一般不提倡使用childNodes
console.log(box.childNodes);
//使用parentNode.children获得元素节点
console.log(box.children);
//1.使用parentNode.firstChild 获取第一个子节点,包含了文本节点
console.log(box.firstChild);
//2.使用parentNode.firstElementChild 获取第一个子节点,不包含了文本节点,ie9才支持
console.log(box.firstElementChild);
//3实际开发的写法,既没有兼容性问题,又返回第一个子元素
console.log(box.children[0]);
console.log(box.children[box.children.length-1]);
</script>
1.2.5.2兄弟节点
nextElementSibling 下一个兄弟元素节点
previousElementSibling 得到上一个兄弟元素节点
//1.nextSibling 下一个兄弟节点,包含元素节点和文本节点等
console.log(div.nextSibling);
//2.nextElementSibling 得到下一个兄弟元素节点 (IE9以上才支持)
//3.previousElementSibling 得到上一个兄弟元素节点 (IE9以上才支持)
//4.自己封装一个函数
<script>
function getNextElementSibling(element){
var el = element;
while(el = el.nextSibling){
if(el.nodeType == 1){
return el.
}
}
return null;
}
</script>
1.2.5.3创建、添加和删除节点
动态创建元素节点
我们想要页面添加一个新的元素:1.创建元素 2.添加元素
<ul>
<li>123</li>
</ul>
<script>
var li = document.createElement('li');
var ul = document.querySelector('ul');
//后面追加元素
ul.appendChild(li);
var lili = document.createElement('li');
ul.insertBefore(lili,ul.children[0]);
//删除节点
ul.removeChild(ul.children[0]);
</script>
1.2.5.4留言案例
<textarea name="" id=""></textarea>
<button>发布</button>
<ul>
</ul>
<script>
var text = document.querySelector('textarea');
var btn = document.querySelector('button');
var ul = document.querySelector('ul');
btn.onclick = function(){
if(text.value==''){
alert('输入为空');
return false;
}
else{
var li = document.createElement('li');
li.innerHTML = text.value+"<a href='javascript:;'>删除</a>";
ul.insertBefore(li,ul.children[0]);
//删除元素
var as = document.querySelectorAll('a');
for(var i = 0;i<as.length;i++){
as[i].onclick = function(){
ul.removeChild(this.parentNode);
}
}
}
}
</script>
1.2.5.5节点复制
深拷贝与浅拷贝
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// cloneNode()不带true则为浅拷贝,只复制标签而不复制里面的内容
var lili = ul.children[0].cloneNode()
// cloneNode(true) 为深拷贝
var lilili = ul.children[0].cloneNode(true);
ul.appendChild(lili);
ul.appendChild(lilili);
</script>
(1)document.write是直接将内容写入页面的内容流,但是文档流执行完毕,则会导致页面全部重绘
(2)innerHTML是将内容写入某个DOM节点,不会导致页面重绘
(3)innerHTML创建多个元素效率更高(不要拼接字符串,采用数组形式拼接),结构稍微复杂
(4)createElement()创建多个元素效率稍低一点点,但是结构更清晰
1.3事件高级
1.3.1 注册事件
(1)传统注册方式;利用on开头的事件onclick
特点:注册事件的唯一性,同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数
(2)方法监听注册方式:
<button>方法监听注册事件</button>
<script>
//事件侦听注册事件,里面的时间类型是字符串,必定加引号,而且不带on
var btn = document.querySelector('button');
btn.addEventListener('click',function(){
alert(22);
})
//同一个元素,同一个事件可以添加多个侦听器(事件处理程序)
btn.addEventListener('click',function(){
alert(33);
})
</script>
1.3.2 解绑事件
(1)传统方式:eventTarget.onclick = null;
(2)方法监听方式:eventTarget.removeEventListener(type,listener[,useCapture]);
<div>1</div>
<script>
var div = document.querySelector('div');
div.onclick = function(){
alert(22);
//1.传统方式
this.onclick = null;
}
//2.removeEventListener方式
div.addEventListener('click',fn);
function fn(){
alert(22);
}
div.removeEventListener('click',fn);
1.3.3 DOM事件流
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流
DOM事件流分为3个阶段:1.捕获阶段 2.当前目标阶段 3.冒泡阶段
事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程
事件捕获:由DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程
<div class="father" style="height: 200px;width: 200px;background-color: aqua;">
<div class="son" style="height: 100px;width: 100px;background-color: rgb(161, 38, 61);">
</div>
</div>
<script>
//1.js代码中只能执行捕获或者冒泡其中的一个阶段
//2.onclick和attachEvent只能得到冒泡阶段
//3.捕获阶段 如果是addEventListenner,第三个参数是true
var son = document.querySelector('.son');
son.addEventListener('click',function(){
alert('son');
},true);
var father = document.querySelector('.father');
father.addEventListener('click',function(){
alert('father');
},true);
//4.冒泡阶段,如果是addEventListenner,第三个参数是false或者为空
var son = document.querySelector('.son');
son.addEventListener('click',function(){
alert('son');
//阻止冒泡,DOM推荐的标准 stopPropagetion()
e.stopPropagation();
},false);
var father = document.querySelector('.father');
father.addEventListener('click',function(){
alert('father');
},false);
</script>
1.3.4 事件对象
事件对象是系统给我们自动创建的,不需要我们传递参数。
事件对象是我们事件的一系列相关数据的集合,比如鼠标点击这个事件中就包含了鼠标的相关信息,比如鼠标坐标,如果是键盘事件就包含键盘事件的信息,比如判断用户按下哪个键
<button>123</button>
<script>
var btn = document.querySelector('button');
//这个事件对象我们可以自己命名,开发中用e就行了
btn.onclick = function(e){
console.log(e);
}
</script>
e.target与this的区别:
(1)e.target返回的是触发事件的对象
(2)this返回的是绑定事件的对象
<ul> 111111
<li>2222222</li>
</ul>
<script>
var ul = document.querySelector('ul');
//onclick绑定的是ul,但是点击li,是li触发的,所以e.target是li
ul.onclick = function(e){
console.log(this);
console.log(e.target);
//阻止默认行为
e.preventDefault();
}
</script>
1.3.5 事件委托
事件委托也称事件代理,在jQuery里面称为事件委派。
原理:不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
例如在ul中的li,我们在ul中注册点击事件,因为点击li,事件会冒泡到ul上,ul有注册事件,就会触发事件监听器,然后利用事件对象的target来找到当前点击的li进行操作
2.BOM
BOM(Browser Object Model)浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window.
window对象有两重角色:
1.JS访问浏览器窗口的一个接口
2.它是一个全局对象,定义在全局作用域中的变量、函数都会变成window对象的属性和方法
window.onload当文档内容完全加载完成会触发该事件,这样就可以 把js代码写到页面元素的上方,因为onload是等页面内容全部加载完毕,再去执行处理函数。
传统注册事件方式只能写一个,以最后一个为准,所以可以使用window.addEventListener,这样就没有限制
<button>按钮</button>
<script>
window.onload = function(){
var btn = document.querySelector('button');
btn.addEventListener('click',function(){
alert('点击了我');
})
}
//load需要等页面内容全部加载完毕,DOMContentLoaded是DOM加载完毕,不包含图片、flash、css等就可以执行,加载速度更快
document.addEventListener('DOMContentLoaded',function() {
alert(33);
})
</script>
2.1 调整窗口大小事件
<div style="width: 200px;height: 200px;background-color: aqua;"></div>
<script>
window.addEventListener('load',function(){
var div = document.querySelector('div');
window.addEventListener("resize",function(){
if(window.innerWidth<=600){
div.style.display = 'none';
}
else{
div.style.display = 'block';
}
})
})
</script>
2.2 定时器
setTimeout(函数名,毫秒数);
//页面中可能有很多的定时器,我们经常给定时器加标识符
//clearTimeout停止定时器
clearTimeout(定时器标识符);
//setInterval 是反复地调用
setInterval(function(){
console.log('继续输出');
},1000);
//clearInterval(定时器标识符);
setTimeout与setInterval的区别:
setTimeout 方法用于在一定的延迟之后执行一次指定的函数。
setInterval 方法用于在一定的时间间隔内重复执行指定的函数
2.3 JS执行机制
JS语言的一大特点就是单线程,后来JS出现了同步和异步。
同步任务都在主线程上执行,形成一个执行栈
JS的异步是通过回调函数实现的,一般有以下三种类型:
(1)普通事件,如click、resize等
(2)资源加载,如load、error等
(3)定时器,包括setInterval、setTimeout等
异步任务相关回调函数添加到任务队列(也称消息队列)中
过程:
(1)先执行执行栈中的同步任务
(2)异步任务放在消息队列中
(3)一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
由于主线程不断地重复获取任务、执行任务、获取任务、执行任务,这种机制被称为事件循环
三、ES6
面相对象
面相对象 vs 面相过程
ES6中的类和对象
在js中,可以使用class关键字声明一个类。对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
类constructor构造函数
和Java等其他面向对象语言类似,没有创建系统会自带一个默认的构造函数
<script>
class Star {
constructor(uname) {
this.uname = uname;
}
}
var ldh = new Star('ldh');
console.log(ldh.uname);
</script>
类中添加方法
<script>
class Star {
constructor(uname) {
this.uname = uname;
} //类中不加逗号
sing(){
console.log('唱歌');
}
}
var ldh = new Star('ldh');
console.log(ldh.uname);
</script>
类的继承
使用:
<script>
class Father {
money() {
console.log(1000);
}
}
class Son extends Father {
}
var son = new Son();
son.money();
</script>
下面代码是有问题的:父类的sum函数用的是this.x,this.y,子类不能直接使用父类的sum(子类继承了父类时,如果定义了构造函数,那么构造函数必须通过 super() 调用父类的构造函数,以确保父类的属性得到正确的初始化。)
<script>
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
var son = new Son(10, 20);
son.sum();
</script>
super关键字
解决办法: 使用super()调用父类的构造函数
class Son extends Father {
constructor(x, y) {
super(x, y); //调用父类的构造函数
}
}
super()不仅可以调用父类的构造函数,还可以调用父类的普通函数
<script>
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
say() {
console.log('我是你爸');
}
}
class Son extends Father {
constructor(x, y) {
super(x, y); //调用父类的构造函数
}
say() {
super.say();
}
}
var son = new Son(10, 20);
son.say();
</script>
子类对象在调用函数时,遵循就近原则,如果子类中有,则调用子类中的函数,子类中没有则调用父类中的函数。但是也使用super关键字来主动去调用父类函数。
super关键字在this之前:
<script>
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
add(x, y) {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
//调用父类的构造函数
//super必须在子类this之前调用
super(x, y);
this.x = x;
this.y = y;
}
sub(x, y) {
console.log(this.x - this.y);
}
}
var son = new Son(10, 20);
son.add();
son.sub();
</script>
如果在子类中把this.x=x和this.y=y;注释掉,则子类中的this将不会被初始化,执行sub实际上使用的是父类的this.x和this.y(因为用了super(x,y))
<script>
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
add(x, y) {
console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
//调用父类的构造函数
//super必须在子类this之前调用
super(x + 1, y + 10);
// this.x = x;
// this.y = y;
}
sub(x, y) {
console.log(this.x - this.y);
}
}
var son = new Son(10, 20);
son.add();
son.sub();
</script>
输出结果为:
类使用时的注意点:
(1)ES6中没有变量提升,所以必须先定义类,才能通过类实例化对象
(2)类中共有的属性和方法一定要加this使用
类里面的this指向问题
constructor里面的this指向实例对象,方法里面的this指向了这个方法的调用者
原型对象this指向:原型对象的this指向的也是对象实例
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
this.btn = document.querySelector('button');
this.btn.onclick = this.add(this.x, this.y);
}
add(x, y) {
//这个add方法里面的this指向的是btn这个按钮,因为这个按钮调用了这个函数
console.log(this);
// console.log(this.x + this.y); //这行输出位undifined
}
}
class Son extends Father {
constructor(x, y) {
//调用父类的构造函数
//super必须在子类this之前调用
super(x, y);
this.x = x;
this.y = y;
}
sub(x, y) {
console.log(this.x - this.y);
}
}
var father = new Father(10, 20);
</script>
如果确实想在add()函数中使用this.x和this.y,则可以在外部定义一个全局变量赋值为constructor中的this。
<button>点击按钮</button>
<script>
var that;
class Father {
constructor(x, y) {
that = this;
this.x = x;
this.y = y;
this.btn = document.querySelector('button');
this.btn.onclick = this.add;
}
add(x, y) {
//这个sing方法里面的this指向的是btn这个按钮,因为这个按钮调用了这个函数
console.log(this);
console.log(that.x + that.y);
// console.log(this.x + this.y);
}
}
class Son extends Father {
constructor(x, y) {
//调用父类的构造函数
//super必须在子类this之前调用
super(x, y);
this.x = x;
this.y = y;
}
sub(x, y) {
console.log(this.x - this.y);
}
}
var father = new Father(10, 20);
</script>
构造函数
静态成员:Star.sex='男'
构造函数原型对象prototype
原型的作用就是共享方法
构造函数通过原型分配的函数是所有对象所共享的。
每一个构造函数都有一个prototype属性,指向另一个对象。
把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法了。
<script>
function Star(name,age){
this.name = name;
this.age = age;
}
Star.prototype.sing=function(){
console.log('我会唱歌');
}
var ldh = new Star('ldh',18);
</script>
对象原型
对象都有一个属性__proto__,指向了我们构造函数的原型对象
//结果为true
console.log(ldh.__proto__ === Star.prototype);
所以方法的查找规则:首先看对象本身是否有该方法,有则执行,没有则因为有__proto__的存在,就去构造函数原型对象prototype身上去查找该方法
它是一个非标准属性,实际开发中,不可以使用这个属性
constructor 构造函数
prototype和__proto__里面都有一个constructor属性,指回了构造函数本身。
所以constructor主要用于记录该对象引用于哪个构造函数
Star.prototype.sing = function () {
console.log('我会唱歌');
}
有时候prototype要放多个函数,可以简写为:
Star.prototype = {
sing: function(){
},
dance: function(){
}
}
但是前一种写法是给prototype增加属性,而后一种是直接整体赋值,这会导致prototype里面失去了constructor属性,所以要增加一个constructor:
Star.prototype = {
constructor: Star,
sing: function () {
},
dance: function () {
}
}
原型链
对象成员查找规则
原型对象的应用:扩展内置对象
可以通过原型对象,对原来的内置对象进行扩展自定义的方法。
案例:通过原型对象给数组提供sum方法:
Array.prototype.sum = function(){
var sum=0;
for(var i=0;i<this.length;i++){
sum+=this[i];
}
return sum;
}
</scrip
继承
ES6之前没有提供extends继承,我们可以通过构造函数+原型对象模拟实现继承,被称为组合继承
call()函数的作用:
(1)调用函数
(2)修改函数的this指向
<script>
function fn(x, y) {
console.log(this);
console.log(x, y);
}
var x = 23;
fn.call(x, 4, 5)
</script>
注意: 第一个参数必须是要修改的this
利用父构造函数继承属性:
<script>
function Father(uname,age){
this.name = name;
this.age = age;
}
function Son(uname,age){
//this指向的是子构造函数的对象实例
//使用call函数,将Father构造函数的this修改为子this
Father.call(this,uname,age);
}
</script>
利用原型对象继承父类型的方法
对于父类的 Father.prototype.money函数,它是写在了原型对象上,而不是写在了构造函数里,所以子类并不能直接继承这个方法。
同时,如果只是使用Son.prototype = Father.prototype,则会导致二者一致了,而通常Son.prototype有其独特的方法,你在Son.prototype增加方法使得Father.prototype也增加了方法。
function Father(uname, age) {
this.name = name;
this.age = age;
}
Father.prototype.money = function () {
console.log(10000);
}
function Son(uname, age) {
//this指向的是子构造函数的对象实例
//使用call函数,将Father构造函数的this修改为子this
Father.call(this, uname, age);
}
Son.prototype = new Father();
Son.prototype.constructor = Son;
Son.prototype.exam = function () {
console.log('考试');
}
console.log(Son.prototype);
</script>
类的本质:
类的本质还是一个函数,类就是构造函数的另一种写法
ES6的类的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已,所以ES6的类其实就是语法糖。
语法糖就是一种便捷写法,简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖
ES5中新增的方法
数组方法
迭代方法:forEach()、map()、filter()、some()、every();
forEach:
<script>
var arr = [1, 2, 3];
arr.forEach(function (value, index, array) {
console.log(value);
console.log(index);
console.log(array);
})
</script>
filter方法
创建一个新数组,新数组中的元素是通过制定数组中符合条件的所有元素,主要用于筛选数组,它返回的是一个新数组
var arr = [13, 234, 323];
var arr_new = arr.filter(function (value, index, array) {
return value >= 20;
})
console.log(arr_new);
some方法
some方法用于检测数组中的元素是否满足制定条件,返回的是一个布尔值,查找到就返回true,查找不到就返回false。
找到满足条件的元素则终止循环
<script>
var arr = [13, 234, 223];
if (arr.some(function (value) {
return value >= 300;
})) {
console.log('有大于300的数');
} else {
console.log('没有大于300的数');
}
</script>
forEach与some的区别:
(1)在some的回调函数中遇到return true就是终止遍历,迭代效率更高
(2)而在forEach的回调函数中return true是不会终止遍历的
filter里面的return也不会终止迭代
every方法
<script>
var arr = [13, 234, 223];
if (arr.every(function (value) {
return value >= 300;
})) {
console.log('arr中全部都是大于300的数');
} else {
console.log('并不全是');
}
</script>
map方法
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
map方法返回一个新数组,而forEach不返回值
字符串方法
trim方法
它并不会影响到原字符串,而是创建了一个新字符串
str.trim();
<script>
var str = ' dsfj ';
console.log(str.trim());
console.log(str);
</script>
对象方法
defineProperty方法
Object.defineProperty(obj,prop,descriptor)
<script>
var obj = {
id: 1,
name: 'cxk'
}
Object.defineProperty(obj, 'age', {
value: 28,
})
console.log(obj);
//writable为false表示属性不可改
Object.defineProperty(obj, 'id', {
writable: false,
})
obj.id = 2;
console.log(obj);
//enumerable
Object.defineProperty(obj, 'name', {
enumerable: false, //值为false 则不允许遍历,默认的值是false
})
console.log(Object.keys(obj));
//configurable为false表明不能被删除,不允许修改第三个参数里的特性,默认为false
Object.defineProperty(obj,'name',{
configurable: false,
})
</script>
函数进阶
函数的定义方式
(1)命名函数:function fn(){};
相当于是第(3)种的实例
(2)匿名函数:var fun = function(){};
(3)利用new Function(‘参数1’,‘参数2’,‘函数体’); 不怎么使用
//必须全部用引号引起来
var fn = new Function('a', 'b', 'console.log(a+b)');
fn(2, 3);
所有的函数都是Function的实例(对象),而实例就有__proto__
函数的调用方式
(1)普通函数: fn(); fn.call();
(2)对象的方法:obj.fn();
(3)构造函数: new Star();
(4)绑定事件函数:btn.onclick=function(){};
(5)定时器函数: setInterval(function(){},1000);
(6)立即执行函数:
立即调用函数(Immediately Invoked Function Expression,IIFE)是 JavaScript 中的一种常见模式,用于创建一个立即执行的函数。这种模式的主要目的是创建一个独立的作用域,以避免变量污染全局作用域,同时执行其中的代码块。
(1)立即执行:函数定义后紧跟一对括号 (),这样函数会立即执行。
(2)独立作用域:函数内部声明的变量是局部变量,不会污染全局作用域。在示例中,localVar 是一个局部变量,只能在函数内部访问。
(3)避免命名冲突:立即调用函数创建了一个独立的作用域,可以防止与全局作用域中已有的变量或函数产生冲突。
<script>
//立即执行函数是自动调用
(function(){
console.log('11');
})()
</script>
函数内部的this指向
改变this指向的方法:
(1)call函数
(2)apply函数:apply函数也是既可以调用函数,又可以改变函数内部的this指向,但是它的参数必须是数组(伪数组)
它的主要应用是:比如利用apply借助于数学内置对象求最大值
<script>
var arr = [23, 3, 343, 23];
console.log(Math.max(arr)); //输出为NaN
console.log(Math.max.apply(null, arr));
</script>
(3)bind函数:不会调用函数,但是能改变函数内部的this指向,返回由指定的this值和初始化参数改造的原函数拷贝
应用场景:有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向
例如我们想设置一个按钮,当点击之后,就禁用这个按钮,3秒后开启这个按钮
以下方法不行:
var btn = document.querySelector('button');
btn.onclick=function(){
this.disabled = true;
//定时器函数里的this指向的是window,而我们要设置的是btn这个按钮
setTimeout(function(){
this.disabled=false;
},3000)
}
改为:
var btn = document.querySelector('button');
btn.onclick=function(){
this.disabled = true;
//定时器函数里的this指向的是window,而我们要设置的是btn这个按钮
setTimeout(function(){
this.disabled=false;
}.bind(this),3000) //将setTimeout里面的回调函数的this改为外面btn的this
}
三个按钮的情况:
var btns = document.querySelectorAll('button');
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
this.disabled = true;
setTimeout(function () {
//不能使用btns[i]的原因是setTimeout要等2秒运行,此时外层i已经++了
// btns[i].disabled=true;
this.disabled = false;
}.bind(this), 2000)
}
}
严格模式
严格模式的使用
js除了提供正常模式外,还提供了严格模式(strict mode)
严格模式对正常的js语义做了一些更改:
(1)消除了js语法的一些不合理、不严谨之处,减少了一些怪异行为
(2)消除了代码运行的一些不安全之处,保证代码运行的安全
(3)提高编译效率,加快运行速度
(4)禁用了在ECMAScript的未来版本中可能会定义的一些语法,如一些保留字:class、enum等不能做变量名
严格模式可以应用到整个脚本或个别函数中。
使用 'use strict'
<script>
'use strict'
//下面的js代码就会按照严格模式执行
</script>
<script>
(function(){
'use strict'
})();
</script>
严格模式的变化
(1)对于变量:
1.变量没有声明就赋值在严格模式下会被禁止
2.严禁删除已经声明的变量。例如 delete x;
语法是错误的。
(2)this指向的问题:
1.严格模式下全局作用域中函数的this是undefined(而之前是window)
2.严格模式下,构造函数不加new调用,this会报错 (之前可以当普通函数,this指向全局对象)
3.new实例化的构造函数还是指向创建的对象实例。
4.定时器this还是指向window
5.事件、对象还是指向调用者
(3)函数变化:
1.函数必须声明在顶层,新版本的js会引入块级作用域,不允许在非函数的代码块内声明函数。
2.函数不能有重名的参数
<script>
function fn(a, a) {
return a + a;
}
console.log(fn(1, 2)); //最终输出结果为4
</script>
更多严格模式的变化请参照以下链接:严格模式
高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。最典型的就是回调函数
闭包
变量作用域:
(1)函数内部可以使用全局变量
(2)函数外部不可以使用局部变量
(3)当函数执行完毕后,本作用域内的局部变量会销毁
闭包指有权访问 另一个函数作用域中的变量 的函数
简单理解,一个作用域可以访问另外一个函数内部的局部变量
闭包的作用:延伸了变量的作用范围
<script>
function fn() {
var num = 10;
function fun() {
console.log(num);
}
return fun;
}
var f = fn();
//上面的赋值相当于是
// var f = function fun(){
// console.log(10);
// }
f();
//此时调用f();执行了console.log(num),相当于访问了fn作用域下的num
</script>
闭包应用-点击li输出当前li的索引号:
以下这种写法是有问题的:onclick是异步,而外层的for循环是同步的,这会导致输出的永远是3
<ul class="nav">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var lis = document.querySelector('.nav').querySelectorAll('li');
for(var i=0;i<lis.length;i++){
lis[i].onclick = function(){
console.log(i);
}
}
</script>
以前的方式:
<script>
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
lis[i].index = i;
lis[i].onclick = function () {
console.log(this.index);
}
}
</script>
现在使用闭包的方式:
<script>
var lis = document.querySelector('.nav').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
(function (i) {
lis[i].onclick = function () {
console.log(i);
}
})(i);
}
</script>
以下没有闭包产生:
<script>
var name = "The window"
var object = {
name: "My Object",
getNameFunc: function () {
return function () {
return this.name;
}
}
}
console.log(object.getNameFunc()());
</script>
以下有闭包产生:
<script>
var name = "The window"
var object = {
name: "My Object",
getNameFunc: function () {
var that = this;
return function () {
return that.name;
}
}
}
console.log(object.getNameFunc()());
</script>
拷贝
浅拷贝与深拷贝
浅拷贝: Object.assign(target,sources);
深拷贝:
function deepCopy(newobj,oldobj){
for(var k in oldobj){
var item=oldobj[k]
if(item instanceof Array){
newobj[k]=[];
deepCopy(newobj[k],item);
}
else if(item instanceof Object){
newobj[k]={};
deepCopy(newobj[k],item);
}
else{
newobj[k]=item;
}
}
}
正则表达式
正则表达式是用于匹配字符串中字符组合的模式,在js中,正则表达式也是对象
一些使用场景
创建正则表达式
(1)通过RegExp对象来创建:var regexp = new RegExp(/123/)
(2)使用字面量来创建: var rg = /123/;
测试正则表达式
var regexp = new RegExp(/123/)
var rg = /123/;
console.log(rg.test(32));
正则表达式中的特殊字符
一个正则表达式可以由简单的字符构成,比如/abc/,也可以是简单字符和特殊字符的组合,比如/ab*c/ 其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如^、$、+等
边界符
边界符用来提升字符所处的位置,主要有两个字符
var rg = /^123/;
var reg = /123$/
var rgg = /^123$/
var rggg = /[abc]/ //[]表明只要包含a,或者包含有b,或者有c都返回true
rggg = /^[abc]$/ //三选一,a,b ,c
rggg = /^[a-z]$/ //26选一
rggg = /^[a-zA-X0-9_-]$/
//[]里面的^不是边界符的含义,而是取反
rggg = /^[^a-zA-X0-9_-]$/
量词符
var reg = /^a*$/; //a取0次或n次
var reg = /^a+$/; //a取1次或n次
var reg = /^a?$/; //a取0次或1次
var reg = /^a{3}$/; //a刚好取3次
var reg = /^a{3,}$/; // a>=3
var reg = /^a{3,16}$/; // a>=3 且 a<=16
var reg =
小括号代表优先级
var reg = /^abc{3}$/ //让c重复3次
var reg = /^(abc){3}$/ //让abc重复3次
正则表达式在线匹配
预定义类
预定义类指的是某些常见模式的简写方式
//座机号码两种格式 010-12345678 0530-1234567
var reg = /^\d{3}-\d{8}| \d{4}-\d{7}$/
正则表达式中的替换
正则表达式参数:/表达式/[switch]
switch有三种值: g表示全局匹配,i表示忽略大小写, gi表示全局匹配+忽略大小写
<textarea name="" id="message"></textarea><button>提交</button>
<div></div>
<script>
var text = document.querySelector('textarea');
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.onclick = function () {
div.innerHTML = text.value.replace(/激情/g, '**');
}
</script>
ES6
ES6实际上是一个泛指,泛指ECMA2015及后续的版本
let
(1)let声明的变量只在所处于的块级才有效
在{}中,使用let关键字声明的变量才具有块级作用域,var是不具备这个特点的。
<script>
if (true) {
let a = 32;
var b = 43;
}
console.log(b);
console.log(a);
</script>
(2)防止循环变量变成全局变量
使用var可以访问
<script>
for(var i=0;i<3;i++){
}
console.log(i); //能输出3
</script>
使用let不能访问
<script>
for (let i = 0; i < 3; i++) {
}
console.log(i);
</script>
(3)使用let声明的变量不存在变量提升
<script>
console.log(i);
let i = 10;
</script>
运行结果为:
<script>
console.log(i);
var i = 10;
</script>
运行结果为undifined
(4)暂时性死区:指的是在代码块内部,尽管变量已经用 let 声明,但在声明之前访问该变量会引发 ReferenceError。这个暂时性死区直到变量声明的位置被执行才会解除。
我的理解是这个作用域内只要用let声明了一个变量,则在这个作用域最前面到这个let变量声明的位置都不能访问这个标识符,即使外层作用域里有同名的额变量
<script>
var num = 123;
if (true) {
console.log(num);
let num = 10;
}
</script>
经典面试题(1):
for循环里面的function并不会立即执行,而是等到arr0调用才执行,而变量i是全局变量,此时函数执行时输出的都是全局作用域下的值。
经典面试题(2):
此题的关键在于每次循环都会产生一个块级作用域,每个块级作用域中的变量都是不同的,函数执行时输出的是自己上一级(也就是循环产生的块级作用域)作用域下的值。
const
作用: 声明常量,常量就是值(内存地址)不能变化的量。
(1)具有块级作用域
(2)声明时必须赋值
(3)常量赋值后,值不可以修改,这有两种情况:
1.基本类型,不可以修改
2.复杂类型,属性,元素可以修改,但是不能整体赋值修改其内存地址
const num = 10;
num = 34; //报错,不能修改
const arr = [3, 23, 32];
arr[0] = 100; //可以修改其中元素的值
arr = [4, 34, 23]; //报错,不能对其重新赋值
var、let、const区别
解构赋值
数组解构
let arr = [23, 34, 435, 32]
let [a, b, c, d, e] = arr;
解构失败的部分则为undifined
let arr = [23, 34, 435, 32]
let [a, b, c, d, e] = arr; //e为undifined
对象解构
第一种写法
let person = {
name: 'cxk',
age: 12
}
let {name,age} = person
第二种写法:
<script>
let person = {
name: 'cxk',
age: 12
}
let {
name: myName,
age: myAge
} = person
console.log(myName);
console.log(myAge);
</script>
箭头函数
ES6新增的定义函数的方式
语法:()=> {}
<script>
const fn = () => {
console.log(10);
}
fn();
</script>
如果函数体中只有一句代码,且代码的执行结果就是返回值,则可以省略大括号
<script>
const sum = (num1, num2) => num1 + num2;
console.log(sum(2, 43));
</script>
如果形参只有一个,那么小括号也可以省略
const fn = v => {
alert(v);
}
fn(3);
箭头函数的this
箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
const obj = {
name: '张三'
};
function fn() {
console.log(this);
//返回的匿名函数在fn函数中(根据上下文),fn函数的this指向的是obj,所以确定this为obj
return () => {
console.log(this);
}
}
const resFn = fn.call(obj);
resFn();
代码执行结果:
箭头函数面试题:
var obj = {
age: 20,
say: () => {
//var不能形成作用域,所以此时的this指向window,而window没有age,所以是undifined
alert(this.age)
}
}
obj.say();
能形成作用域的一些情况:
(1)函数:每个函数都会创建自己的作用域。
(2)块级作用域:使用 let 或 const 关键字可以在块级作用域内声明变量,如 if 语句、for 循环、while 循环以及花括号 {} 中
(3)模块:在 ES6 中,通过 import 和 export 可以创建模块作用域
四.jQuery
jQuery官网
下载文件后在html中导入: