✴️大家好,我是王同学,爆肝三天三夜王同学把JavaScript 知识点梳理了一遍,文章没有一点套路,只有满满的干货
✴️如果对你有帮助就给我点个赞吧,这样我们就互不相欠了
✴️星光不负赶路人,所有的幸运都来自于坚持不懈的努力,大家一起冲冲冲~~~
变量的声明提升:你可以提前使用一个稍后才声明的变量,而不会引发异常
在执行所有代码前,JS有预解析阶段,会预读所有变量的定义
所有数字不分大小、不分整浮、不分 正负、都是数字类型
NaN是英语“not a number” 的意思,即不是一个数字,但它是一个数字类型的值
加号可以拼接多个字符串
一些时候需要用到空字符串,直接书写闭合的引号对即可
字符串的length属性表示字符串的长度
作用:得到指定位置的字符
一个没有被赋值的变量的默认值是undefined,而undefined的类型也是undefined,即undefined又是值,又是一种类型,这种类型只有它自己一个值
在变量声明提升时,变量的值也是undefined
3
true
NaN
Infinity
bcd
bc
''
bc
算术、关系、逻辑、赋值、综合
默认情况下,乘除法的优先级要高于加法和减法,必要时可以使用圆括号来改变运算的顺序
如果参与运算的某操作数不是数字型,那么JavaScript会自动将此操作符转换为数字
隐式转换的本质就是内部调用Number()函数
解决办法:在进行小数运算的时候,要调用数字的toFixed ()方法保留指定的小数位
JavaScript中没有提供幂计算,开根号的运算符,需要使用Math对象相关的方法进行计算
判断变量 a是不是介于3到15之间,应该怎么写呢?
置反运算的结果一定是布尔值
执行函数体中的所有语句,就称为函数调用
调用函数十分简单,只需要在函数名字后面书写圆括号对即可
和变量声明提升类似,函数声明也可以被提升
函数的表达式是不能被提升的,如果函数是用函数表达式的写法定义的,则没有提升的特性
<script>
//首先函数会优先提升
//变量的定义后提升 后提升并不会把先提升的覆盖掉
//变量提升只会先提升定义 不会提升值
fun();
var fun = function() {
alert('A');
}
function fun() {
alert('B')
}
fun();
</script>
参数是函数内的一些特定的值,在调用函数时,必须传入这些参数的具体的值
函数的参数有多有少,函数可以没有参数,也可以有多个参数,多个参数之间要用逗号隔开
<script>
function fun() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
console.log('所有的参数之和为:' + sum); //110
}
fun(33, 22, 55)
</script>
函数体内可以使用return关键字表示“函数的返回值”
//函数的功能是返回两个参数的和
function sum(a, b) {
return a + b;
}
var result = sum(3, 4);
console.log(result);
调用一个有返回值的函数,可以被当做一个普通值,从而可以出现在任何可以书写的地方
调用函数时,一旦遇见return语句则会立即退出函数,将执行权交还给调用者
1
A
B
2
结合if语句 往往不需要写else分支了
<script>
//书写一个函数 函数的功能是判断一个数字是否是偶数
function checkEven(n) {
if (n % 2 == 0) return true;
return false;
}
var result = checkEven(7);
console.log(result); //false
</script>
<script>
//计算一个数字的阶乘
function factorial(n) {
//累乘器
var result = 1;
for (var i = 1; i <= n; i++) {
result *= i;
}
return result;
}
//穷举法
for (var i = 100; i <= 999; i++) {
// 把数字i变为字符串
var i_str = i.toString();
var a = Number(i_str[0]);
var b = Number(i_str[1]);
var c = Number(i_str[2]);
// 根据喇叭花数来判断
if (factorial(a) + factorial(b) + factorial(c) == i) {
console.log(i);//145
}
}
</script>
这个函数中的a、b分别表示数组中靠前和靠后的项,如果需要将他们交换位置,则返回正数,否则就返回负数
<script>
var arr = [3, 5, 6, 1, 2];
arr.sort(function(a, b) {
return a - b;
})
console.log(arr);
var arr = [99, 55, 66, 77];
arr.sort(function(a, b) {
return b - a;
})
console.log(arr);
</script>
函数的内部语句可以调用这个函数自身,从而发起对函数的一次迭代。
在新的迭代中,又会执行调用函数自身的语句,从而又产生一次迭代,当函数执行到某一次时,不再进行新的迭代,函数被一层一层返回,函数被递归递
<script>
//书写一个函数 函数会自己调用自己 从而形成递归
//函数的功能就是计算某个阶乘 n的阶乘就是n*(n-1)的阶乘
function factorial(n) {
//递归的出口 如果计算1的阶乘,可以不用递归了 直接告诉你答案1
if (n == 1) return 1;
//如果询问的不是1的阶乘就返回n*(n-1)!
return n * factorial(n - 1);
}
var result = factorial(6);
console.log(result); //720
</script>
<script>
// 编写一个函数 这个函数的功能就是返回斐波那契数列中下标为n的那项的值
function fib(n) {
//出口 数列的小标为0和下标为1的项的值都是1
if (n == 0 || n == 1) return 1;
// 斐波那契数列的本质特征就是每一项都等于前面两项的和
return fib(n - 1) + fib(n - 2);
}
//书写一个循环语句 计算斐波那契数列的前15项
for (var i = 0; i < 15; i++) {
console.log(fib(i));
}
</script>
<script>
//原数组
var arr1 = [33, 44, 11, 22, [77, 88],
[11, 999]
];
//结果数组 "每一个都有一个结果"
var result = [];
//函数 这个 函数会被递归
function deepClone(arr) {
//遍历数组的每一项
for (var i = 0; i < arr.length; i++) {
//类型判断 如果遍历到的项是数组
if (Array.isArray(arr[i])) {
//递归
result.push(deepClone(arr[i]));
} else {
//如果遍历到的项不是数组,是基本类型,就直接推入到结果数组中
//相当于是递归的出口
result.push(arr[i])
}
}
//返回结果数组
return result;
}
//测试一下
var arr2 = deepClone(arr1);
console.log(arr2);
//是否藕断丝连
console.log(arr1[4] == arr2[4]);
arr1[4].push(99);
console.log(arr1)
console.log(arr2);
</script>
使用递归的思想,整体思路和浅克隆相似,但稍微进行一些改动,如果遍历到项是基本类型值,则直接推入结果数组,如果遍历到的项是又是数组,则重复执行浅克隆的操作。
<script>
//准备原数组
var arr1 = [33, 44, 55, 66, [99, 22], 111];
//准备一个结果数组
var result = [];
//遍历原数组
for (var i = 0; i < arr1.length; i++) {
result.push(arr1[i]);
}
//输出结果数组
console.log(result);
// 测试是否实现了克隆 ,就是说本质上是内存中的不同数组了
console.log(arr1 == result); // false
//测试这样的克隆是浅克隆“藕断丝连”
arr1[4].push(100);
console.log(result);
</script>
JavaScript是函数级作用域编程语言:变量只在其定义时所在的function内部有意义
如果不将函数定义在任何函数的内部,此时这个变量就是全局变量。它在任何函数内都可以被访问和更改
遮蔽效应:如果函数中也定义了和全局同名的变量,则函数内的变量会将全局的变量遮蔽
<script>
var a = 10;
function fun(a) {
a++;
console.log(a); //8
}
fun(7);
console.log(a); //10
</script>
一个函数内部也可以定义一个函数,和局部变量相似,定义在一个函数内部的函数是局部函数
作用域链:在函数嵌套中,变量会从内到外逐层寻找他的定义
<script>
var a = 10;
var b = 20;
function fun() {
var c = 30;
function inner() {
var a = 40;
var d = 50;
console.log(a, b, c, d); //40 20 30 50
}
inner();
}
fun();
</script>
在初次给变量赋值的时候,如果没有加var, 则将定义全局变量
<script>
var a = 1;
var b = 2;
function fun() {
//字母c没有加var 关键字 所以他为全局变量
c = 3;
var b = 4;
b++;
c++;
}
fun();
console.log(b); //2
//在函数的外部是可以访问变量c的
console.log(c); //4
</script>
什么是闭包:闭包是函数本身和该函数声明时所处的环境状态的组合 ,函数能够“记忆住”其定义所处的环境,即使函数不在其定义的环境中被调用,也能访问定义时所处的环境变量
<script>
//创建一个函数
function fun() {
//定义局部变量
var name = '慕课网';
//返回一个局部函数
return function() {
alert(name)
}
}
//调用外部函数就能得到内部函数
var inner = fun();
//定义一个全局变量
var name = 'ABC';
//执行inn函数 就相当于在 fun函数的外部执行了内部的函数
inner()
</script>
在JavaScript中,每次创建函数时都会创建闭包
但是,闭包往往需要将函数换一个地方执行,才能被观察出来
<script>
function createCheckTemp(standardTemp) {
function checkTemp(n) {
if (n <= standardTemp) {
alert("你的体温正常")
} else {
alert("你的体温偏高")
}
}
return checkTemp;
}
//创建一个 checkTemp函数,它以37.1为标准线
var checkTemp_A = createCheckTemp(37.1);
//创建一个 checkTemp函数,它以37.3为标准线
var checkTemp_B = createCheckTemp(37.3);
checkTemp_A(37.2);
checkTemp_B(37.2);
</script>
<script>
//封装一个变量 这个函数的功能是私有化变量
function fun() {
// 定义一个局部变量 a
var a = 0;
return {
getA: function() {
return a;
},
add: function() {
a++;
},
pow: function() {
a *= 2;
}
}
}
var obj = fun();
//如果想在fun函数外面使用变量a唯一的办法就是调用getA() 方法
console.log(obj.getA());
//想让 a进行加一操作
obj.add();
console.log(obj.getA());
obj.pow();
console.log(obj.getA());
</script>
不能滥用闭包,否则会造成网页的性能问题,严重时可能会导致内存泄漏,所谓内存泄漏是指程序中已动态分配的内存由于某种原因未释放或者无法释放
<script>
function addCount() {
var count = 0;
return function() {
count = count + 1;
console.log(count);
}
}
var fun1 = addCount();
var fun2 = addCount();
fun1(); //1
fun2(); //1
fun2(); //2
fun1(); //2
</script>
IIFE(Immediately Invoked Function Expression立即调用函数表达式)是一种特殊的JavaScript函数写法,一旦被定义,就立即被调用
函数不能直接加圆括号被调用
为变量赋值:当给变量赋值需要一些较为复杂的计算的时候(如if语句)使用IIFE显得语法更紧凑
IIFE可以在一些场合(如for循环)将全局变量变为局部变量语法显得更紧凑
//要求输入三位数
var n = Number(prompt('请输入三位数'));
//对用户输入的值进行合法验证
if (!isNaN(n) && 100 <= n && n <= 999) {
var a = parseInt(n / 100);
var b = parseInt(n / 10) % 10;
var c = n % 10;
if (Math.pow(a, 3) + Math.pow(b, 3) + Math.pow(c, 3)) {
alert('是水仙花数');
}
} else {
alert('您输入的数字不合法')
}
switch语句的用途:当一个变量被分类讨论的情形
switch语句并不像if语句那样当执行了某个分支后会自动跳出if语句,我们必须主动调用break语句来跳出break语句体,如果不书写break,则后面的所有case都将被视为匹配,直到遇见break
<script>
//让用户输入月份
var month = Number(prompt('请输入月份'));
//分类讨论
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
alert('这个月有31天');
break;
case 4:
case 6:
case 9:
case 11:
alert('这个月有28天');
break;
case 2:
alert('这个月有28天或者29天');
break;
default:
alert('你输入的月份有误')
}
</script>
三元运算符的用途:根据某个条件是否成立,在两个不同值中选择变量的值
<script>
//sum是累加器 初始值是0
for (var i = 1, sum = 0; i <= 100; i++) {
sum += i;
}
console.log(sum); //5050
</script>
//穷举法
for (var i = 1; i < 100; i++) {
if (i % 3 == 1 && i % 4 == 2 && i % 5 == 3) {
console.log(i); //58
}
}
while语句也是一种循环语句,是一种不定范围的循环,和for循环各有不同的用武之地,几乎所有的循环都同时提供for循环和while循环
while语句事先不指定循环的开始,结束的范围,只要测试条件满足,就一直执行循环
while循环没有显示定义循环变量,必须自己在while循环外先定义好循环变量,有时候甚至没有循环变量
var i = 1;
while (i <= 100) {
console.log(i);
i++;
}
//寻找满足n的平方和大于456789的最小整数
//穷举法 从1开始试验
var n = 1;
while (n * n < 456789) {
n++;
}
console.log(n); //676
var n = 1;
var sum = 0;
while (sum < 500) {
sum += n;
n++;
}
//注意这里有个“出一错误”
console.log(n - 1); //32
break表示立即终止这个循环,他只能用在循环语句中,在for循环和while循环中都可以使用
continue用于跳过循环中的一个迭代,并继续执行循环中的下一个迭代,for循环中经常使用continue
<script>
//后判断的一种形式 至少执行一次循环体
do {
console.log('★');
} while (false); //★
</script>
var n = 1;
do {
console.log(n);
n++
} while (n <= 100);
<script>
do {
var dx = parseInt(Math.random() * 9) - 4;
var dy = parseInt(Math.random() * 9) - 4;
}
while (dx == 0 && dy == 0);
console.log(dx, dy);
</script>
<script>
//随机一个数字2-99
var answer = parseInt(Math.random() * 98) + 2;
//此时范围的最大值和最小值这个数字是用来提示用户的
var min = 1;
var max = 100;
//不断的重复 就用死循环
while (true) {
//询问用户猜测的数字
var n = Number(prompt('请猜猜数字' + min + '~' + max));
//验证用户输入的数字是否在范围内
if (n <= min || n >= max) {
alert('你输入的数字不在范围内')
//放弃这次循环,就开启下一次迭代
continue;
}
//判断用户输入的数字和answer的关系
if (n > answer) {
alert('你输入的数字太大了');
//因为用户输入的数字较大,所以可以让此时的最大范围的数字变为用户输入的值
max = n;
} else if (n < answer) {
alert('你输入的数字太小了')
//因为用户输入的数字较小,所以可以让此时的最小范围的数字变为用户输入的值
min = n;
} else {
alert('猜对了');
//结束死循环
break;
}
}
</script>
算法是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出
算法就是把一个问题,拆解为计算机能够一步一步执行的步骤
//用户输入数字n
var n = Number(prompt('请输入数字'));
//累加器
var sum = 0;
//遍历分母
for (var i = 2; i <= n; i++) {
sum += (i + 1) / i
}
alert(sum.toFixed(2));
<script>
//计算阶乘
var n = Number(prompt('请输入数字'));
//累乘器 从1开始 如果要从0开始 0乘以任何数字都为0
var result = 1;
//倒着遍历 计算阶乘
for (var i = n; i >= 1; i--) {
result *= i;
}
alert(result);
</script>
计算机最突出的能力就是计算,他没有归纳总结,推理逻辑的能力,所以人们使用计算机解决问题的时候,要扬长避短,充分发挥计算机的优势,而不要让他们进行逻辑推理,穷举法就是这样一种算法思想。
穷举法,顾名思义,是指根据题目的条件确定答案的大致范围,并在此范围内对所有可能的情况逐一验证,直到全部的情况验证完毕,若某个情况符合题目的条件,则为本题的一个解,若全部情况验证后都不符合条件,则本题无解。
//穷举法
for (var i = 1; i <= 100; i++) {
if (i % 3 == 0 && i % 5 == 0) {
console.log(i);
}
}
//寻找约数
var n = Number(prompt('请输入数字:'));
//穷举法
for (var i = 1; i <= n; i++) {
if (n % i == 0) {
console.log(i);
}
}
//寻找水仙花数
for (var i = 100; i < 1000; i++) {
var i_str = i.toString();
var a = i_str.charAt(0);
var b = i_str.charAt(1);
var c = i_str.charAt(2);
if (Math.pow(a, 3) + Math.pow(b, 3) + Math.pow(c, 3) == i) {
console.log(i);
}
}
所谓的访问元素节点就是指得到,获取页面上的元素节点
对节点进行任何的操作,就是要得到节点,访问节点主要依靠document对象
文本节点也属于节点
在标准的W3C规范中,空白文本节点也应该算作节点,但是在IE8以及以前的节点中也会有一定的兼容问题,他们不把空文本节点当做节点
改变元素节点中的内容可以使用两个相关的属性
innerHTML属性能够以HTML语法设置节点中的内容
innerText属性只能以纯文本的形式设置节点中的内容
<body>
<div id="box">
<p>我是原本的段落0</p>
<p>我是原本的段落1</p>
<p>我是原本的段落2</p>
</div>
<script>
var oBox = document.getElementById('box');
var oPs = oBox.getElementsByTagName('p');
//创建孤儿节点
var oP = document.createElement('p');
//设置内部文字
oP.innerHTML = '我是新来的'
//上树
//oBox.appendChild(oP);
oBox.insertBefore(oP, oPs[0])
</script>
</body>
什么是事件:用户与网页的交互动作
比如:当用户点击元素时,当鼠标移动到元素上时,当文本框的内容被改变时、当键盘在文本框中被按下时,当网页加载完毕时……
监听,顾名思义,就是让计算机能够发现这个事件发生了,从而执行程序员预先编写的一些程序
<style>
div {
width: 200px;
height: 200px;
background-color: #ccc;
}
</style>
</head>
<body>
<div id="box">
</div>
<script>
var oBox = document.getElementById('box');
//给这个盒子添加点击事件监听
oBox.onclick = function() {
alert('你好,我是点击事件的函数');
}
</script>
什么是事件:用户与网页的交互动作
比如:当用户点击元素时,当鼠标移动到元素上时,当文本框的内容被改变时、当键盘在文本框中被按下时,当网页加载完毕时……
监听,顾名思义,就是让计算机能够发现这个事件发生了,从而执行程序员预先编写的一些程序
<style>
div {
width: 200px;
height: 200px;
background-color: #ccc;
}
</style>
</head>
<body>
<div id="box">
</div>
<script>
var oBox = document.getElementById('box');
//给这个盒子添加点击事件监听
oBox.onclick = function() {
alert('你好,我是点击事件的函数');
}
</script>
<script>
var box1 = document.getElementById("box1");
var box2 = document.getElementById("box2");
var box3 = document.getElementById("box3");
// box1.onclick = function() {
// alert('A');
// }
// box1.onclick = function() {
// alert('B '); //B
// }
box2.addEventListener("click", function() {
alert('C ');
}, false)
box2.addEventListener("click", function() {
alert('D '); //先C再D
}, false)
</script>
事件处理函数提供一个形式参数,它是一个对象,封装了本次事件的细节,这个参数通常用单词event或者字母e来表示
<style>
* {
margin: 0;
padding: 0;
}
#box {
width: 200px;
height: 200px;
background-color: #333;
margin: 100px;
padding: 50px;
}
body {
height: 2000px;
}
#info {
font-size: 30px;
}
</style>
</head>
<body>
<div id="box">
</div>
<div id="info">
</div>
<script>
var oBox = document.getElementById('box');
var oInfo = document.getElementById('info');
oBox.onmouseover = function(e) {
oInfo.innerHTML = 'offsetX / Y: ' +
e.offsetX + ',' + e.offsetY + '
' + 'clientX/Y:' + e.clientX + ',' + e.clientY;
}
</script>
<script>
var oList = document.getElementById('list');
var lis = oList.getElementsByTagName('li');
// 书写循环语句 批量添加监听
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function() {
this.style.color = ' red';
}
}
</script>
每一个事件监听注册都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗十分的大
实际上,每个li 的事件处理函数都是不同的函数,这些函数本身也会占用内存
<button id="btn">按我创建一个新列表项</button>
<ul id="list">
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</ul>
<script>
var oList = document.getElementById('list');
var oBtn = document.getElementById('btn')
oList.onclick = function(e) {
//e.target表示用户真正点击的那个元素
e.target.style.color = 'red';
// e.currentTarget表示整个列表项
// e.currentTarget.style.color = 'blue';
}
oBtn.onclick = function() {
//创建新的li元素
var oLi = document.createElement('li');
//写内容
oLi.innerText = ' 我是新来的';
//上树
oList.appendChild(oLi);
}
</script>
什么是具名函数? 什么是匿名函数?
具名函数,故名思意就是有名字的函数,具名函数一般是我们定义的最多的一种
// 函数
function fn() {
console.log('我是具名函数')
console.log('看,我有名字')
console.log('我的名字叫 fn')
}
fn()
fn()
没有名字的函数,匿名函数我们也随处可见,我们来写几个匿名函数
setTimeout(function () {
console.log('我是匿名函数,我被用于定时器参数传递')
}, 1000)
let arr = [1, 2, 3]
arr.forEach(function (item) {
console.log('我是匿名函数,我被用于回调函数参数')
})
let box = document.querySelector('#box')
box.onclick = function () {
console.log('我是匿名函数,我被用于绑定事件')
}
<h1 id="info"></h1>
<button id="btn1">开始</button>
<button id="btn2">暂停</button>
<script>
var oInfo = document.getElementById('info')
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var a = 0;
//设置一个全局变量
var timer;
btn1.onclick = function() {
//为了防止定时器叠加 我们应该在设置定时器之前清除定时器
clearInterval(timer)
//更改全局变量的值为一个定时器实体
timer = setInterval(function() {
oInfo.innerText = ++a;
}, 1000)
}
btn2.onclick = function() {
clearInterval(timer)
}
</script>
<button id="btn1">2秒后弹出你好</button>
<button id="btn2">2秒后取消弹出</button>
<script>
var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var timer;
btn1.onclick = function() {
timer = setTimeout(function() {
alert('你好,世界!');
}, 2000)
}
btn2.onclick = function() {
clearInterval(timer);
}
</script>
以上结果就意味着,多个JS文件之间是共享作用域的,即JS文件没有作用域隔离的功能
常用的操作就是模拟浏览器的回退按钮
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
height: 5000px;
background-image: linear-gradient(to bottom, blue, green, yellow);
}
.backtotop {
width: 60px;
height: 60px;
background-color: rgba(255, 255, 255, 0.6);
position: fixed;
bottom: 100px;
right: 100px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="backtotop" id="backtotopbtn">返回顶部</div>
<script>
var backtotopbtn = document.getElementById("backtotopbtn");
var timer;
backtotopbtn.onclick = function() {
//设置表先关
clearInterval(timer);
//设置定时器
timer = setInterval(function() {
//让scrollTop不断减少
document.documentElement.scrollTop -= 80;
// 定时器停下来
if (document.documentElement.scrollTop <= 10) {
clearInterval(timer);
}
}, 20)
}
</script>
</body>
</html>
对象是键值对的集合,表示属性和值的映射关系
可以使用“点语法”访问对象中指定键的值
如果属性不符合JS标识符的命名规范,则必须使用方括号的写法来访问
直接使用赋值运算符重新对某属性赋值即可更改属性
如果对象本身没有某个属性值,则用点语法赋值时,这个属性就会被创建出来
如果要删除某个对象的属性,需要使用delete操作符
如果某个属性值是函数,则它也被称为对象的方法
方法和函数,只不过方法是对象的函数属性,它需要用对象打点调用
和遍历数组类似,对象也可以被遍历,遍历对象需要使用for…in…循环,使用for…in循环需要可以遍历对象的某个键
<script>
var obj1 = {
a: 1,
b: 2,
c: [33, 44, {
m: 55,
n: 66,
p: [77, 88]
}]
};
//实现深克隆
function deepClone(o) {
//要判断o是对象还是数组
if (Array.isArray(o)) {
//数组
var result = [];
for (var i = 0; i < o.length; i++) {
result.push(deepClone(o[i]))
}
} else if (typeof o == "object") {
//对象
var result = {
};
for (var k in o) {
//递归之后的原来的值
result[k] = deepClone(o[k]);
}
} else {
//基本类型值
var result = o;
}
return result;
}
var obj2 = deepClone(obj1);
console.log(obj2);
//看是否有藕断丝连的情况
//引用类型的值互不相等
console.log(obj1.c == obj2.c); //false
obj1.c.push(99);
console.log(obj2); //obj2不变 因为没有藕断丝连的现象
obj1.c[2].p.push(99);
console.log(obj1);
</script>
函数中可以使用this关键字,它表示函数的上下文
与中文中的这类似,函数中的this具体指代什么必须通过调用函数时的前言后语来判断
由以上可知,函数的上下文是由函数的调用方式来决定的,也就是说同一个函数,用不同的形式来调用它,则函数的上下文不同
情形一:对象打点调用函数,函数中的this指代这个打点的对象
情形二:圆括号直接调用函数,函数中的this指代的是window对象
输出3
输出NaN
规则二:圆括号直接调用函数,则函数的上下文是window对象
规则三:数组(类数组对象)枚举出函数进行调用,上下文是这个数组(类数组对象)
所有键名为自然数序列(从0开始),且有length属性的对象
arguments对象是最常见的类数组对象,它是函数的实参列表
规则五:定时器,延时器调用函数,上下文是window对象
输出结果:7
JS规定,使用new操作符调用函数会进行四步走:
Java、C++等是“面向对象”(object-oriented)的语言
JavaScript是“基于对象(object-based)的语言”
JavaScript中的构造函数可以类比于OO语言中的类,写法的确类似,但和真正OO语言还是有本质的不同
in运算符只能检查某个属性或者方法是否可以被对象访问,不能检查是否是自己的属性或者方法
实现继承的关键在于:子类必须拥有父类的全部属性和方法,同时还应该能定义自己特有的属性和方法,使用特有的原型链特性来实现继承,是普遍的做法
面向对象的本质:定义不同的类,让类的实例工作
面向对象的优点:程序编写更清晰,代码结构更加严密,使代码更健壮易于维护
面向对象经常用到的场合,需要封装和复用性的场合(组件思维)
包装类的目的就是为了让基本类型值可以从他们的构造函数的prototype上获得方法
一、什么是正则表达式
二、如何创建正则表达式
var 正则表达式的名称=new RegExp('正则表达式内容','修饰符');
var 正则表达式的名称=/ 正则表达式内容/修饰符;
三、正则表达式的常用方法—test方法
作用:验证字符串是否符合正则表达式的格式要求
格式:正则表达式.test(字符串)
返回值:true(符合要求)、false(不符合)
四、正则表达式的构成
(一)普通字符
(二)、特殊字符(元字符)
(1)、定位符
^:表示以某个字符开头,如/^a/表示字符串以字母a开头
$:表示以某个字符结尾,如/$a/表示字符串以a结尾
(2)、表示数量的限定符
*:
表示它前面的字符的个数为0到无穷多个,如/a*/表示a的个数最少0个,最多无限个
+:表示他前面的字符的个数为1到无穷多个。如/a+/表示 a的个数最少1个,最多无限个
?:表示他前面的字符为0到1个。如/a?/表示a的个数最少0个,最多一个
{n}:表示他前面的字符只能是n个.如/a{3}/表示字符串中包含3个字符a
{n, }:表示他前面的字符为n到无穷多个,如/a{3, }/表示字符串 a的个数大于等于3个
{n,m}:表示他前面的字符的个数为n-m个.如a{1,5}表示a的个数为1-5个
(3)、转义字符
转义字符:所谓转义字符就是指在普通字符的前面加反斜线,使得他具有特定的功能和含义,也可以在具有特定的功能和含义的字符前面加反斜线,将他转换为普通字符 如:\d表示任意一个数字/\表示将正则表达式中的斜线转换为普通的斜线
\d:表示任意一个数字
\D:表示任意一个非数字
\w:表示任意一个字母、数字、下划线
\W:任意一个非字母数字下划线
\s:表示任意一个空白符,比如制表符、空格
\S:任意一个非空白符
(4)、备选字符集
[值1 值2 值3 值4]
:中括号里面的值表示字符串中可以匹配到的任意一个值
[值1-值n]:
表示值1到值n间的任意一个值为备选选项
[^值1 值2 值3]
:表示字符串中不可以包含中括号里面的任意一个值
[\u4e00-\9fa5]
:表示任意的一个汉字
四、正则表达式的匹配模式
1)贪婪模式:指正则表达式在对字符串进行匹配时尽可能多的匹配字符,默认就是贪婪模式
2)懒惰模式:指正则表达式在对字符串进行匹配时尽可能少的匹配字符,默认是贪婪模式,如果要将贪婪模式转化为懒惰模式,需要在数量修饰符后面加问号
(五)、字符串对象中和正则表达式结合使用的方法
(一)、split方法
字符串名称.splite('分隔符');
(二)、match方法
(三)、repalce方法
作用:替换字符串中指定的子串
格式:字符串.repalce.('要被替换的子串','新的子串');
返回值:默认情况下,只替换符合条件的第一个子串,如果要将所有的子串替换掉,则需要使用正则表达式 且这个正则表达式需要使用修饰符g,如果要替换的子串不存在则返回原串
(四)、search方法
indexOf
方法字符串.search('子串');
六、正则表达式常用的修饰符