Javascript(进阶)
1、预处理阶段
LE:预处理阶段
<!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>
</head>
<body>
<script>
// LE : 预处理阶段
// function fa(){
// var f = 123;
// console.log(52); //这里执行 结果为1
// }
// var f = 1;
// console.log(f);
// f();
// function f(){
// console.log('foodoir');
// }
// function f(){
// console.log('hello word');//这里后面的函数调用会顶替前面的函数
// }
// console.log(a); //undefined,函数作用域提升
// //console.log(b); //不存在
// console.log(c); //函数体,函数在任意地方都可以调用
// console.log(d); //undefined
// var a = 1;
// b = 2;
// console.log(b); //2
// function c(){
// console.log('c');
// }
// var d = function(){
// console.log('d');
// }
// console.log(d) //函数体
// //词法环境里面分析
// function f(a,b){
// alert(a); //函数体
// alert(b); //2
// var b = 100;
// function a(){} //函数的优先级要高于变量
// }
// f(1,2);
// 所有东西都要先声明在调用!
function a(){
function b(){
g = 12; //函数内的没有使用var 声明的变量是全局变量
}
b();
console.log(g); //12
}
//js程序的bug 过马路 维护
</script>
</body>
</html>
2、作用域
<!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>
</head>
<body>
<button>测试一</button>
<button>测试二</button>
<button>测试三</button>
<script>
var btns = document.getElementsByTagName("button");
for (let i = 0; i < btns.length; i++) { //如果使用var
btns[i].onclick = function(){ //这里改成this.onclick
console.log('第'+(i+1)+'个')
}
}
</script>
<script>
// // 内层作用域可以访问外层作用域,反之则不行!
// var a = 1 ;
// function aa(){
// // bb(); //undefined
// var b = 2 ;
// function bb(){
// console.log(a);
// console.log(b);
// }
// bb(); //2
// }
// aa();
</script>
</body>
</html>
3、变量提升
<!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>
</head>
<body>
<script>
// console.log(ad); //把调用变量的操作放在了声明的前面
// var ad = 2;
//预处理阶段
//var ad; undefined
// var foo = 11; //外部的变量不会影响内部的!!!
// function n(){
// // undefined true
// if(!foo){
// var foo = 5;
// }
// console.log(foo); //5
// }
// n();
// var foo = 3; //混淆你的视线
// function h(){
// console.log(foo);
// var foo = foo || 5; //函数默认值的赋值
// console.log(foo); //5
// }
// h();
// aa();
// function aa(){
// console.log(1);
// bb(); //函数提升是必要的而且是js规定好的 变量提升是js的语言的问题 开发人员不遵守规则!
// function bb(){
// console.log(22);
// }
// }
// aa();
// // foo(); //函数表达式不能进行函数提升
// var foo = function(){
// console.log(1);
// }
// foo();
// //需要提前获取返回值的时候 就是用函数声明 因为他可以进行函数提升
// //如果是普通的函数 没有返回值 使用两种就都可以
// //变量提升是不应该存在的,我们应该在写程序的时候先声明再调用
// //封装好的类库 应该包裹在{} 它才会形成自己的作用域不会和其他人的类和其他人的代码冲突
// 验证偶数
function isEven(n){
if( n === 0){
return true;
}
return isOdd(n - 1);
}
//验证奇数
function isOdd(n){
if(n === 0 ){
return false;
}
return isEven(n - 1);
}
console.log(isEven(2)); //true
//变量提升是bug 函数提升解决的是循环调用 递归问题
/*
注意:一定要先声明后调用!!!
*/
</script>
</body>
</html>
4、自由变量
<!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>
</head>
<body>
<script>
// var a =100;
// function fn(){
// var b = 200;
// // console.log(a); //这里的a就是一个自由变量
// console.log(b);
// function fn2(){
// console.log(a); //这里的a就是一个自由变量
// }
// fn2(); //作用域链,一层一层的向外寻找
// }
// var x = 10;
// function fn(){
// console.log(x);
// }
// function show(f){
// var x = 20;
// (function(){
// f()//10 而不是20 向外层作用域查找
// })()
// }
// show(fn)
var a = 10;
function fn(){
var a = 30;
console.log(a); //变量名冲突问题 静态作用域
}
fn();
function show(){
var a = 20;
fn(); //在哪里创建在哪里寻找
}
show();
</script>
</body>
</html>
5、数据类型
<!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>
</head>
<body>
<script>
alert(0.1+0.2)
//根本原因是小数转换为2进制数据存储到计算机里面的时候
//会产生循环问题,解决的办法就是进行截取
//截取之后的二进制数据进行相加之后,再转换为十进制变成了0.300000004
//整数转换的时候不会出现问题
alert((0.1*10+0.2*10)/10);
//小数位的情况是不知道的
alert((0.01*10+0.02*10)/10);
//通过程序自动判断有多少位 扩大相应的倍数 在运算 再除以!
/*
解决方案:
一:Number.toFixed(num)
num必需。规定小数的位数,是 0 ~ 20 之间的值,包括 0 和 20,有些实现可以支持更大的数值范围。如果省略了该参数,将用 0 代替。
console.log((0.1 + 0.2).toFixed(12) == 0.3)
二:math库
*/
</script>
</body>
</html>
6、声明
<!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>
</head>
<body>
<ul id="ulList">
</ul>
<script>
window.onload = function(){
ul = document.getElementById("ulList");//解决方法:var ul = ...
console.log(ul);
for (var i = 0; i <= 5; i++) {
//创建一个li对象
var li = document.createElement("li");
//li标签内内容设置为:Itemi
li.appendChild(document.createTextNode("Item"+i));
//声明一个会计变量j,并将i赋值给j
let j = i;
//绑定点击事件
li.onclick = function(){
alert("Item"+i+"is chicked.")
};
ul.appendChild(li);
}
console.log(ul); //内存泄露 变量不会死亡 内存得不到释放!前提是ul没有定义!
}
</script>
</body>
</html>
7、reverse
<!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>
</head>
<body>
<script>
// //反转字符串
// var str = 'abcdefg';
// function reverseString(str){
// //split('') 将字符串变成数组
// console.log(str.split(''));
// //reverse()反转方法
// console.log(str.split('').reverse())
// //join 将数组变成字符串
// console.log(str.split('').reverse().join(''))
// return str.split('').reverse().join('');
// }
// reverseString(str)
// ------------------------------------------
//使用桟的先进后出原则:
//例:最先打进来的电话在最下面!
function Stack(){
this.data = []; //保存桟内的元素
this.top = 0; //记录桟顶的位置
}
//在桟的原型链上添加方法
Stack.prototype = {
//入栈,现在的栈顶加入元素 元素的个数改变
push:function push(element){
this.data[this.top++] = element;
},
//出栈,先返回栈顶的元素 元素的个数减一
pop:function(){
return this.data[--this.top];
},
//返回桟内的元素长度
length:function(){
this.top;
}
}
//然后通过自定义的函数调用桟在实现输出
function reverseString2(str){
//new 创建一个stack实例
var s = new Stack();
//将字符串转换为数组
var arr = str.split('');
//存储转换后的数据
var result ='';
//数组的长度
var len = arr.length;
//将元素压入桟内
for(var i = 0;i < len; i++){
s.push(arr[i]);
}
//将元素输出
for(var j= 0;j < len; j++){
result += s.pop(j);
}
return result;
}
var str = 'abcdefg';
console.log(reverseString2(str));
</script>
</body>
</html>
8、递归
<!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>
</head>
<body>
<script>
/*
假设递归已经写好了
sun(100);
sum(n)+sum(n-1)+sum(n-1)
判断临界条件 终止条件
n == 1 return 1
将临界条件加入到递归体中
例:求1-100的和
function sum(n){
if(n == 1) return 1;
return sum(n-1)+n;
}
console.log(sum(100));
*/
/*
假设递归已经写好了 求:1,3,5的奇数和!
sun(5);
寻找递归体的关系
sum(n-2)+n; 奇数取余2 != 0 偶数取余2 == 0
判断临界条件 终止条件
if(n == 1) return 1;
将临界条件加入到递归体中
function Sum(n){
if(n % 2 == 0){
n--;
}
if(n == 1){
return 1;
}else{
return n + Sum(n-2);
}
}
var sum = Sum(5);
console.log('5以内的奇数和为=='+sum);
*/
/*
斐波那契额数列
fib(n);
找递归的关系
fib(n) = fib(n-1)+fib(n-2);
临界条件
if(n == 0) return 1;
if(n == 1) return 1;
将临界条件加入到递归体中
function fib(n){
if(n ==0 || n==1) return 1;
return fib(n-1)+fib(n-2);
}
console.log(fib(5));
*/
/*
例:阶乘!!!
假设已经写好了
factorial(4);
找递归的关系
factorial(n) = factorial(n-1) * n ;
临界条件
if(n == 1) return 1;
将临界条件加入到递归体中
function factorial(n){
if(n == 1) return 1;
return factorial(n-1) * n ;
}
console.log(factorial(4));
*/
/*
求幂:
n 的 m次方就是m个n相乘即n乘以(m-1)个n相乘
power(n,m);
递归关系
power(n,m-1)*n
临界条件
if(m == 1) return n; //发现这个不需要!!
if(m == 0) retuen 1;
将临界条件加入到递归体中
function power(n,m){
if(m == 0) return 1;
return power(n,m-1) * n;
}
console.log(power(5,3));
*/
/*
使用递归实现字符串逆序
递归从最后一个索引开始,通过charAt()函数获取一个字符,并拼接到结果字符串中,递归的结束条件是位置索引小于0.
例:
var str = 'abcdefg';
反转字符串
reverseString3(str);
寻找递推关系
charAt() 返回的是具体的字符串
sortReverse = reverseString3.charAt(str.length--)
终止条件
(str.length-- < 0)
将临界条件加入到递归体中
function reverseString3(str,len,strOut){
//终止条件
if(len < 0){
return strOut;
}
strOut += str.charAt(len--);
//console.log(strOut);
//这里的len要是函数内部的数据变换的
return reverseString3(str,len,strOut);
}
var str = 'abcdefg';
reverseString3(str,str.length-1,result);
var result = '';
console.log(reverseString3(str,str.length-1,result));
*/
</script>
</body>
</html>
9、统计字符串中出现最多的字符和次数
<!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>
</head>
<body>
<script>
/*
// 算法一:通过key:value存储出现的 次数,然后逐个判断出现次数最大值,同时获取对应的字符
// 通过key: value,key表示不重复出现的字符,value表示字符出现的次数
// 思路:遍历每个字符串的每个字符,判断是否出现在key中,如果在对应的value加1,如果不在,则直接新增一组key-value,value值为1,
// 得到value后,遍历该对象,逐个比较value值的大小,找出最大的value,并记录!
function getMax(str){
//创建一个空对象
var json = {};
//循环遍历字符串
for(var i = 0;i<str.length;i++){
if(!json[str.charAt(i)]){
//如果不存在,添加到json对象中
json[str.charAt(i)] = 1;
}else{
//如果存在 value加1
json[str.charAt(i)]++;
}
}
// console.log(json);
//存储次数和具体的字母
var maxChar = '';
var maxNum = 0;
//遍历json对象
for (var key in json) {
if (json[key]>maxNum) {
//让当前内容更新
maxChar = key;
maxNum = json[key];
}
}
//返回值
return '出现最多的值是:' + maxChar + '出现最多的次数为:' + maxNum;
}
var str = 'helloJavascripthellohtmlhellocss';
console.log(getMax(str));
*/
// 算法二:
// 主要思想是对字符串进行排序,然后通过lastIndexOf)函数获取索引值后,判断索引值的大小以获取出现的最大次数。
// 1.首先将字符串处理成数组,调用sort()函数进行排序,处理成字符串。
// 2.然后遍历每个字符,通过调用lastIndexOf()函数, 确定每个字符出现的最后位置,然后减去当前遍历的索引,就可以确定该字符出现的次数。
// 3.确定字符出现的次数后,直接与次数最大值变最maxCount进行比较,如果比maxCount大,则直接更新maxCount的值,并同步更新maxCountChar的值,如果比maxCount小。 则不做任何处理。
// 4.计算完成后,将索引值设置为字符串出现的最后位置,进行下一轮计算,直到处理完所有字符。
function getMax2(str){
//处理字符串变成数组 排序后在变成字符串
var str = str.split('').sort().join('');
// console.log(str)
var defaultNum = 0;
var defaultChar = '';
//不同写法的for循环的length
for(var i = 0,j=str.length;i<j;i++){
var char = str[i];
console.log(char);
//计算每个字符出现的位置 索引的值 0123 跟长度差一位所以 +1
var maxNum = str.lastIndexOf(char)-i+1;
// console.log(maxNum);
//判断出现次数的最大值比较
if(maxNum>defaultNum){
defaultNum = maxNum;
defaultChar = char;
}
//变更索引位置
i = str.lastIndexOf(char);
}
//返回值
return '出现最多的值是:' + defaultChar + '出现最多的次数为:' + defaultNum;
}
var str = 'helloJavascripthellohtmlhellocss';
console.log(getMax2(str));
</script>
</body>
</html>
10、去重
<!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>
</head>
<body>
<script>
/*
算法1:
的主要思想是使用key-value类型的对象存储,key表示唯一-的字符, 处理完后将所有的key拼接在一起即可得到去重后的结果。
1.首先通过key-value形式的对象来存储数据,key表示不重复出现的字符,value为boolean类型的值,为true则表示字符出现过。
2.然后遍历字符串,判断当前处理的字符是否在对象中,如果在,则不处理,如果不在, 则将该字符添加到结果数组中。
3.处理完字符串后,得到一个数组,转换为字符串后即可获得最终需要的结果。
*/
function removeRepeat(str){
//创建对象
var json = {};
// 创建一个数组
var result = [];
//遍历循环 将单独的字符添加到对象中
for(var i=0;i<str.length;i++){
// console.log(json[str[i]]);
if(!json[str[i]]){
//将数据添加到json对象中
json[str[i]] = true;
result.push(str[i]);
}
}
// 数组转字符串方便 通过数组操作
// console.log(json);
// 将对象转换为字符串
return result.join('');
}
var str = 'helloJavascripthellohtmlhellocss';
console.log(removeRepeat(str));
/*
算法二 :
的主要思想是借助数组的filter()函数,然后再filter)函数中使用indexOf()函数判断。
1.通过call()函数改变filter()函数的执行体,让字符串可以直接执行filter()函数。
2.在自定义的filter()函数回调中,通过indexOf()函数判断其第一-次出现的素引位置,如果与filter()函数中的index-样,则表示第一次出现, 符合条件则return出去。这就表示只有第一次出现的字符会被成功过滤出来,而其他重复出现的字符会被忽略掉。
3. filter()函数返回的结果便是已经去重的字符数组,将其转换为字符串输出即为最终需要的结果。
*/
function removeRepeat2(str){
//使用call()函数改变filter()函数的执行主体
let result = Array.prototype.filter.call(str,function(char,index,arr){
//通过indexOf()函数与idex的比较,判断是否是第一次出现的字符
return arr.indexOf(char) === index;
});
return result.join('');
}
var str = 'helloScripthellohtmlhellocss';
console.log(removeRepeat2(str));
</script>
</body>
</html>
11、判断为空的方法
<!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>
</head>
<body>
<script>
// //判断一个变量是否为空,注意== 和 ===的区别
// var obj ;
// if(obj == null){} //判断null或者undefined
// if(obj === undefined){} //只能判断undefined
// var obj = {};
// console.log(obj);
// if(obj){
// console.log('是真的');
// }else{
// console.log('是假的');
// }
// var obj = {};
// //判断函数是否为空的函数
// function isEmpty(obj){
// for(let key in obj){
// if(obj.hasOwnProperty(key)){
// return false;
// }
// }
// return true;
// }
// //true 是空的对象
// console.log(isEmpty(obj));
// function Person(){};
// Person.prototype.name = 'teacher';
// //使用new操作实例化对象
// var pbj = new Person;
// console.log(isEmpty(pbj));
// //判断是否为空数组
// var arr = []; [] {} 使用 new Array() 或 [] 创建出来的数组对象,都会拥有 Object.prototype 的属性值。伪数组!
// arr.instanceof Array && arr.length === 0;
//字符串为空
var str = '';
if(str === '' || str.trim().length == 0){
console.log(1);
}
</script>
</body>
</html>
12、数组
<!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>
</head>
<body>
<script>
// // var arr = [];
// // var arr = {};
// var num = 'sss';
// console.log(typeof num); //typeof针对基本数据类型的判断
// //判断数组还是对象
// var arr = [];
// var obj = {};
// function test(){};
// console.log(arr instanceof Array); //instanceof用在引用数据类型
// console.log(arr instanceof Object);
// console.log(typeof test);
//封装后 这里可以把这些方法提取一个工具类
// var arr = 'qqq';
// function getDateType(o){
// if(o instanceof Array){
// return 'Array';
// }else if(o instanceof Object){
// return 'Object';
// }else{
// return 'Data not is Array or Object';
// }
// }
// console.log(getDateType(arr))
//判断是否是数组
var a = [];
var b = [1,2,3];
var c = new Array();
console.log(Array.isArray(a));
console.log(Array.isArray(b));
console.log(Array.isArray(c));
</script>
</body>
</html>
13、filter
<!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>
</head>
<body>
<script>
/*
filter()函数过滤满足条件的数据,返回一个新的数组,不会改变原来的数组!
返回值为true 元素被添加到新的数组中,返回值为false不会 添加到新的数组中,返回空数组!
*/
// var arr = [1,2,3,4,5,6,7,8,9];
// //过滤函数调用的函数 过滤规则
// var oaa = arr.filter(filterOval);
// function filterOval(x){
// // console.log(x);
// return x%2;
// }
// console.log(oaa);
// 实例:求大于18岁的男生
var arrObj = [{gender:'男',age:19},{gender:'女',age:14}];
var result = arrObj.filter(funage); // 如果需要在筛选就用result过滤,以此类推!!!
//过滤函数
function funage(obj){
return obj.gender == '男' && obj.age>18;
}
console.log(result);
</script>
</body>
</html>
14、函数累加器
<!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>
</head>
<body>
<script>
/*
reduce ()函数 累加器
arr.reduce(callback, [, initalvalue]);
initialValue用作callback的第一个参数值, 如果没有设置,则会使用数组的第一个元素值 。
callback会接收4个参数(accumulator, currentValue, currentIndex, array )。
1. accumulator表示 上一一次调用累加器的返回值,或设置的initiaValue值。 如果设置了initialValue, 则accumulator=initialValue;否则accumulator=数组的第一个元素值 。
2. currentValue表示数组正在处理的值。
3. currentindex表示当前正在处理值的索引。如果设置了initialValue 则cunrentIndex从0开始,否则从1开始。
4. array表示数组本身。
*/
// var arr = [1,2,3,4,5,6];
// var sum = arr.reduce(function(accumulator,currentValue){
// return accumulator+currentValue;
// })
// console.log(sum);
/*
统计数组中每个元素出现的次数
存在一个数组[1,2,2,3,3,3,4,4,4,4]通过一 定的算法统计出其中的数字出现的次数。
我们利用key-value对象的形式,统计出每个元素出现的次数,其中key表示数组元素,value表示出现的次数。
将initialValue设置为一个空对象,initialValue作为累加器accumulate的初始值, 依次往后执行每个元素,
如果执行的元素在accumulate中存在,则将其计数加1,否则将当前执行元素作为accumulate的key,其value值为1,
依次执行完所有元素之后,最终返回结果。
*/
var arr = [1,2,3,4,4,4,5,5,5,5];
var dd = function(arr){
// console.log(arr);
// return 返回内容加入到对应的对象中
return arr.reduce(function(accumulator,currentValue){
// console.log(accumulator);
// console.log(currentValue);
// 三元运算符 :如果正确就返回 ?后面的值 ,如果不是就返回:后面的 = 1;
accumulator[currentValue] ? accumulator[currentValue]++ :accumulator[currentValue] = 1;
return accumulator;
},{})
}
console.log(dd(arr))
</script>
</body>
</html>
15、最大值最小值
<!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>
</head>
<body>
<script>
var arr = [1,2,3,4,35,6,5,6];
//在原型链上绑定一个方法
Array.prototype.min = function(){
var min = this[0];
var len = this.length;
for(var i=0;i<len;i++){
if(this[i]<min){
min = this[i];
}
}
return min;
}
console.log(arr.min());
Array.prototype.max = function(){
var max = this[0];
var len = this.length;
for(var i=0;i<len;i++){
if(this[i]>max){
max = this[i];
}
}
return max;
}
console.log(arr.max());
// js Math.min是math对象本身就存在的
// console.log(Math.max(8,10));
/*
借助Math对象的min()函数和max()函数
算法2的主要思想是通过apply()函数改变函数的执行体,将数组作为参数传递给pply)函数,
这样数组就可以直接调用Math对象的min()函数和max()函数来获取返回值。
*/
// apply(,[]) 改变执行主体 就是用apply方法
// call(,) 不是数组的时候 ,就使用call方法
Array.min = function(arr){
return Math.min.apply(Math,arr);
}
Array.max = function(arr){
return Math.max.apply(Math,arr);
}
// console.log(Array.min(arr));
// console.log(Array.max(arr));
console.log(Math.max(8,10));
console.log(Math.min(8,10));
// 链式调用
var arr = [1,2,3,4,35,6,5,6];
Array.prototype.min = function(){
return Math.min.apply(Math,this);
}
Array.prototype.max = function(){
return Math.max.apply(Math,this);
}
console.log(arr.min());
console.log(arr.max());
</script>
</body>
</html>
16、数组遍历
<!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>
</head>
<body>
<script src="./polyfill.js"></script>
<script>
// var res = '';
// var arr = [0,1,2,3,4,5,6,7];
// for(var i=0;i<arr.length;i++){
// res += arr[i];
// }
// // join solit
// // push pop shift unshift length 数组的方法
// // indexOf lastIndexOf length 字符串的方法
// console.log(res.split(''))
// forEach()函数算是在数组实例方法中用于遍历调用次数最多的函数,forEach()函数接收一个回调函数,
// 参数分别表示当前执行的元素值,当前的索引和数组本身,在方法体中输出每个数组元素即可完成遍历!
var arr = [1,2,3,4,5,6];
arr.forEach(function(element,index){
console.log(element);
})
/*
forEach函数是在ES5中新添加的,它不兼容只支持低版本的js浏览器,这是我们需要提供一个polyill.,
polyfill就是为不支持js新特性的浏览器打补丁,我们可以通过手动创建polyill文件加载到项目中,达成兼容低版本的兼容,也可以采用成型的包例如: babel-polyfill, polilio。
通过for循环,在循环中判断this对象, 即数组本身是否包含遍历的索引,如果包含则利用call()函数去调用回调函数,传入回调函数所需的参数。
*/
var arr = [1,2,3,4,5,6];
arr.forEach(function(element,index,array){
console.log(element);
})
</script>
</body>
</html>
polyill.js
// forEach() 函数的polyfill
Array.prototype.forEach = Array.prototype.forEach ||
function(fn,context){
for(var k=0,length = this.length;k<length;k++){
if(typeof fn === 'function' && Object.prototype.hasOwnProperty.call(this,k)){
fn.call(context,this[k],k,this);
}
}
};
var arr = [1,2,3,4,5,6];
arr.forEach(function(element,index,array){
console.log(element);
})
17、数组去重
<!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>
</head>
<body>
<script>
// 数组去重的第一种方法
// function arrayRepeat(arr){
// var result = [];
// for(var i=0;i<arr.length;i++){
// if(result.indexOf(arr[i]) === -1){
// result.push(arr[i]);
// }
// }
// return result;
// }
// var arr = [1,2,2,3,,3,4,4,4,5,5];
// console.log(arrayRepeat(arr));
// 数组去重的第二种方法
var arr = [1,2,2,3,3,3,4,4,4];
function arrayRepeat2(arr){
// var result = [];
//result 要是一个数组元素,默认有一个值
var result = [arr[0]];
// console.log(result);
arr.sort(function(a,b){return a-b});
for(var i = 0;i<arr.length;i++){
if(arr[i] !== result[result.length-1]){
result.push(arr[i]);
}
}
return result;
}
console.log(arrayRepeat2(arr))
</script>
</body>
</html>
18、Date
# 关于日期可以使用一下两种类库
Moment.js 与 Data.js
需要注意日期合法校验!
19、函数的定义和调用
<!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>
</head>
<body>
<script>
// 函数声明
function a(){
console.log(1);
return true;
}
console.log(a());
//函数表达式
var sum = function(){
console.log(2);
}
sum();
// 使用new操作符
var add = new Function("num1","num2","return num1+num2");
console.log(add(1,2));
// ————————————
var y = 'global';
function construct(){
var y = 'local';
return new Function('return y');
}
console.log(construct()());
// 代码模块化
/*
* person
* getName type:'string [] {} number'
* setName newName
* return name
*/
var Person = (function(){
var _name = '';
return{
getName:function(){
return _name;
},
setName:function(newName){
_name = newName;
}
}
}());
Person.setName('zhao teacher');
console.log(Person.getName());
// ----------
var obj = {
name:'zhao teacher',
getName:function(){
return name;
}
}
obj.getName();// or obj[getName()];
// ----如果某个方法返回的是函数对象本身this,那么可以利用链式调用--------
var obj = {
name:'zhao teacher',
getName:function(){
console.log(this.name);
},
setName:function(name){
this.name = name;
return this; //在函数内部返回函数对象本身
}
};
obj.setName('zhao teacher2').getName();
// 构造器调用模式
// 构造器调用模式会定义一个函数,在函数中定义实例属性,在原型上定义函数,然后通过new操作符生成函数的实例,在通过实例调用原型上定义的函数!
// 定义一个函数对象
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.getName = function(){
return this.name;
}
Person.prototype.getAge = function(){
return this.age;
}
// 实例化函数对象
var p = new Person('zhao',3);
//实例化调用方法
p.getName();
p.getAge();
// 匿名函数
// var a = function(){
// console.log(1);
// }
// a();
// (function(){
// console.log(1);
// }());
// function test(x,y){
// y = 5 || y; // 参数默认赋值的问题
// }
</script>
</body>
</html>
20、函数参数
<!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>
</head>
<body>
<script>
a = 1; // 基本数据类型 存的具体的值
b = [1,2,3]; // 引用数据类型 地址
function test(a,b,c){
a = 2;
console.log(2);
b[0] = 2;
console.log(b);
}
test(a,b);
console.log(a);
console.log(b);
// 形参的值是可以改变的 但是形参的改变不会影响实参的值的改变 基本数据类型的情况
// 引用数据类型 就可以改变了
// 伪数组只有长度的方法 而其他数组的方法都不能使用
function foo(x,y,z){
// 现在传入实际参数的个数
// console.log(arguments.length);
if(arguments.length == 0){
console.log('不能处理,没有参数');
}else{
console.log('已经开始处理');
}
}
foo(1);
// arguments 索引 length
function test(){
// console.log(arguments[0]);
// console.log(arguments[1]);
// console.log(arguments[2]);
for(var i=0;i<arguments.length;i++){
console.log(arguments[i]);
}
}
test(1,2,3);
// arguments.callee属性
// arguments对象有一个特殊的属性callee,表示的是当前执行的函数,在比较时是严格相等的
function foo(){
console.log(arguments.callee === foo);
}
foo();
// 匿名递归函数调用
function aa(){
return function(n){
if(n<=1){
return 1;
}
return n*arguments.callee(n-1);
}
}
console.log(aa()(5));
// 判断传入的参数去处理函数
function aa(x,y,z){
if(arguments.length == 3){
console.log('正在处理')
}else{
console.log('请传入三个参数')
}
}
aa(1,2,3);
// 对传入的任意个参数个数进行处理
function aa(str){
// console.log(str);
// console.log(arguments[1]);
var strArr = Array.prototype.slice.call(arguments,1);
// console.log(strArr);
// console.log(strArr.join(str));
return strArr.join(str);
}
console.log(aa('-','blue','red','yellow','green'));
// 函数重载
function sum(){
var arr = Array.prototype.slice.call(arguments);
return arr.reduce(function(acc,cur){
return acc+cur;
},0)
}
console.log(sum(1,2,3))
</script>
</body>
</html>
21、闭包
<!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>
</head>
<body>
<script>
/*
对于闭包的概念,官方有一个通过的解释: -一个用于许多变量和绑定了这些变量执行上下文环境的表达式,通常是一个函数。
闭包有两个很明显的特点:
1.函数拥有的外部变量的引用,在函数返回时,该变量仍然处于活跃状态。
2.闭包作为一个函数返回时,其执行上下文环境不会被销毁,仍然处于执行上下文环境中。
在JavaScript中存在一种内部函数,即函数声明和函数表达式可以位于另一个函数的函数体内,在内部函数中可以访问外部函数声明的变量,当这个内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
*/
// 例:闭包
// 函数 里面有一个函数 里面的函数要引用外部的东西
// function fn(){
// var max = 10;
// return function bar(x){
// if(x>max){
// console.log(x);
// }
// }
// }
// var f1 = fn();
// // console.log(f1);
// f1(11);
/*
闭包的用途:
一:结果缓存
在开发过程中,我们可能会遇到这样的场景,假如有一个处理很耗时的函数对象,每次用都会消耗很长时间。
我们可以将其处理结果在内存中缓存起来。这样在执行代码时,如果内存中有,则直接返回,如果内存中没有,则调用函数进行计算,更新缓存并返回结果。
因为闭包不会释放外部变量的引用,所以能将外部变量值缓存在内存中。
*/
// 对结果的缓存是一段很长时间的耗时操作
var cacheMemory = (function(){
//定义一个缓存的容器
var cache = {};
console.log(cache);
return{
set:function(id){
if(id in cache){
return '查找结果是id:'+cache[id];
}
// 如果内存中没有就要查询
var result = fn(id);
// console.log(result);
cache[id] = result;
// 返回的cacheid
return '查找结果是id:'+cache[id];
}
}
})();
function fn(id){
console.log('很耗时间的数据库操作!');
return id;
}
console.log(cacheMemory.set(1));
console.log(cacheMemory.set(1));
// 封装
// 为桟添加方法
var Stack = (function(){
// 使用数组模拟桟
var arr = [];
// 为桟添加方法
return {
push:function(value){
arr.push(value);
},
pop:function(){
arr.pop();
},
size:function(){
return arr.length;
}
}
})();
Stack.push('abc');
console.log(Stack.size());
// --------定时器问题-------
// 方法一:
// var arr = ['one','two','there'];
// for(let i=0;i<arr.length;i++){
// // setTimeout(function(){
// // console.log(arr[i]);
// // },i*1000)
// setTimeout(() => {
// console.log(arr[i])
// }, i*1000);
// }
// 方法二:
var arr = ['one','two','there'];
for(var i=0;i<arr.length;i++){
(function(time){
setTimeout(() => {
console.log(arr[time]);
}, time*1000);
})(i);
}
// 作用域链问题
var name = 'outher';
var obj = {
name :'inner',
method:function(){
_this = this; // 使用_this就指向自身,否则就是向最外层寻找!
return () =>{
return _this.name;
}
}
};
console.log(obj.method()());
/*
闭包的优点:
1.保护函数内变量的安全,实现封装,防止变量流入其他环境发生命名冲突,造成环境污染。2.在适当的时候,可以在内存中维护变量并缓存,提高执行效率。
闭包的缺点:
1.消耗内存,通过来说,函数的活动对象会随着执行的上下文环境一起被销毁, 但是由于闭包引用的是外部函数的活动对象,因此这个活动对象无法被销毁,这意味着闭包比普通函数要消耗更多的内存。
2.内存泄漏,在IE9之前, 如果闭包的作用域链中存在DOM对象,则意味着该DOM对象无法被销毁,造成内存泄漏。
*/
// DOM: 因为事件处理的函数很多,就会占用大量内存,进而影响性能,所以设置为将element手动设置为null。
function closure(){
var element = document.getElementById('elementID');
// 使用临时变量进行存储
var id =element.id;
element.onclick = function(){
console.log(id);
}
//手动将元素设置为null
element = null;
}
</script>
</body>
</html>
22、_ this
<!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>
</head>
<body>
<script>
// 函数声明
function Person(name){
this.name = name ;
console.log(this);
}
// // 实例化
// var p = new Person('zhao teacher');
// // 调用
// console.log(p.name);
// Person('teache'); // 全局对象
// console.log(window.name);
// this 指向全局对象
// var value = 10;
// var obj = {
// value:100,
// set:function(){
// console.log(this.value);
// }
// }
// obj.get(); // this指向的就是调用的那个对象
// // 指向全局对象的实例
// var value = 10;
// var obj = {
// value:100,
// method:function(){
// var foo = function(){
// console.log(this.value); //10
// }
// foo();
// console.log(this.value); //100
// }
// }
// obj.method();
// this指向对象实例:当通过new操作符调用构造函数生成对象实例时,this指向该实例。
var number = 10;
function Person(){
// 复写全局变量
number = 20;
// this
this.number = 30;
}
Person.prototype.getName = ()=> {
return this.number;
}
var p = new Person();
console.log(p.number);
console.log(p.getName());
// this重新绑定对象
var value = 10;
var obj = {
value:20
}
var method = function(){
console.log(this.value);
}
// method(); window
// console.log(method.call(obj)); //再没有第二个参数的情况下与apply相同
method.apply(obj);
var newMethod = method.bind(obj);
// console.log(newMethod);
newMethod();
/*
闭包中的this:
函数的this只能被自身访问,其内部函数无法访问,因此在遇到闭包的时候,闭包内部的this关键字无法访问到外部变量函数的this变量
*/
// 如果没有_this就在全局声明sport不然就是undefined
// sport:'basketball';
// var user = {
// sport:'basketball',
// data:[
// {name:'qinjiang',age:3},
// {name:'qinjiang',age:4},
// ],
// clickHandler:function(){
// console.log(this);
// var _this = this;
// // 此时的this指向的是user 对象
// this.data.forEach(function(person){
// console.log(this);
// console.log(person.name+'is playing'+_this.sport);
// })
// }
// };
// user.clickHandler();
// 加深this理解
function f(k){
this.m = k;
return this;
}
// 这里f(1) 只有在console的时候才是window,不然什么都不是,this.m=window所以是undefined
var m = f(1);
// console.log(m);
// 在这里打印可以看出f(2)是window所以this是2
var n = f(2);
// console.log(n);
console.log(m.m); // undefined
console.log(n.m); // 2
</script>
</body>
</html>
23、call、apply、bind
<!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>
</head>
<body>
<script>
/*
cal()函数调用一个函数时,会将该函数的执行对象上下文改变为另一个对象,其语法如下:
function. call(thisarg, arg1,arg2);
。function为需要调用的函数
. thisArg表示的是新的对象上下文,函数中的this将指向thisArg, 如果thisArg为null或者undefined, 则this会指向全局对象。
. arg1, arg2, ..表示的是函数所接收的参数列表。
*/
// 定义一个add函数 call()
function add(num1,num2){
console.log(num1+num2);
}
// 定义myAddCall()函数
function myAddCall(x,y){
// 绑定add()函数 使用的是单个的值
return add.call(this,x,y);
}
myAddCall(1,2);
// ---------------------------
// 定义一个add函数 apply()
function add(num1,num2){
console.log(num1+num2);
}
// 定义myAddCall()函数
function myAddCall(x,y){
// 绑定add()函数 使用的是数组
return add.apply(this,[x,y]);
}
myAddCall(1,2);
// ----------------------------
// 定义一个add函数 bind()
function add(num1,num2){
console.log(num1+num2);
}
// 定义myAddCall()函数
function myAddCall(x,y){
// 绑定add()函数 使用的是单个的值
var bindfn = add.bind(this,x,y); //这里就是区别!!
return bindfn();
}
myAddCall(1,2);
// ---------------------------------
// 求数组的最大项和最小项
var arr = [1,4,5,7,8,9,5];
console.log(Math.max.apply(null,arr));
console.log(Math.min.apply(null,arr));
// 类数组转换为数组对象 arguments是类数组,还有通过document.getElementsByTagName也是类数组!
// 求和函数 slice返回一个新的数组
function sum(){
var arr = Array.prototype.slice.call(arguments);
return arr.reduce(function(arr,cur){
return arr+cur;
})
}
console.log(sum(1,2,3,4));
// 执行匿名函数
var animals = [
{species:'Lion',name:'kirito'},
{species:'Whale',name:'AI'}
];
// 遍历数组中对象的所有属性
for(var i=0;i<animals.length;i++){
(function(i){
this.print = function(){
console.log(this['species']+this['name']);
}
this.print();
}).call(animals[i],i)
}
// 绑定函数配合setTimeout
function LateBloomer(){
this.petalCount = Math.ceil(Math.random()*12)+1;
}
// 定义一个原型链上的方法
LateBloomer.prototype.bloom = function(){
setTimeout(this.declare.bind(this),1000)
}
LateBloomer.prototype.declare = function(){
console.log('随机数'+this.petalCount);
}
// 生成实例
var s = new LateBloomer();
s.bloom();
</script>
</body>
</html>
24、对象的属性和访问方式
<!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>
</head>
<body>
<script>
/*
数据属性:
1. [[Configurablel]:表示属性是否可以通过delete删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是true.
2. [Enumerablel]:表示属性是否可以通过for-in循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是true.
3. [Writable]l:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是true.
4. [Valuel]:包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为undefined.
*/
// Object.defineProperty 一旦调用这个方法就必须要用这个方法去修改,不然就会报错!
/*
如果要修改属性的默认特性,就必须使用Object.defineProperty()方法。
这个方法接收3个参数:要给其添加属性的对象、属性的名称和一个描述符对象。最后一一个参数,
即描述符对象上的属性可以包含: configurable、enumerable、writable 和value,跟相关特性的名称一对应。
根据要修改的特性,可以设置其中一个或多个
*/
var person = {};
// 只读
Object.defineProperty(person,"name",{
writable:false,
value:'kirito'
})
console.log(person.name);
person.name = 'koko';
console.log(person.name);
/*
访问器属性:
1. [[Configurablel]:表示属性是否可以通过delete删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是true.
2. [Enumerablel]:表示属性是否可以通过for-in循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是true.
3. [[Get]]:获取函数,在读取属性时调用。默认值为undefined。
4. [[Set]]:设置函数,在写入属性时调用。默认值为undefined。
*/
/* js
伪私有成员 js_
公共成员 js
私有成员 _js
*/
// 访问器属性是不能直接定义的,必须使用 Object.defineProperty();
var person = {
_age:10
};
Object.defineProperty(person,"age",{
get:function(){
return this._age;
},
set:function(newValue){
if(newValue>10){
this._age = newValue;
console.log('设置成功!');
}
}
});
console.log(person.age);
person.age = 9;
console.log(person.age);
person.age = 12;
console.log(person.age);
// 点和中括号的区别在操作数值上
var person = {
1:123,
name:'kkkk'
}
console.log(person[1]);
var myname = 'name';
console.log(person.name);
console.log(person[myname]); //不加引号 当变量解析 加引号就是字符串
console.log(person['name']);
// 如果属性名包含引号 就不能使用.操作符 使用[]
</script>
</body>
</html>
25、创建对象
<!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>
</head>
<body>
<script>
// 普通的对象创建方法 与字面量一样!
var obj = {
name:'aa',
age:3,
set:function(){
console.log(this.name);
}
}
// obj.name = 'bb';
// 构造对象 Object()函数生成实例
var person = new Object();
person.name = 'kk';
person.age = 1;
person.getName = function(){
return this.name;
}
person.address = {
name:'上海',
code:'122'
};
// -------------------------------
// 基于工厂方法模式 : 这种方法可以解决很多重复的代码,但是创建的实例都是object无法区分具体类型!
function createPerson(name,age,address){
var o = new Object();
o.name = name ;
o.age = age;
o.address = address;
o.getName = function(){
return this.name;
}
return o;
}
var person = createPerson('k',3,{
name:'上海',
code:0000
});
console.log(person);
// 基于构造函数创建对象
function Person(name,age,address){
this.name = name;
this.age = age;
this.address = address;
this.getName = function(){
return this.name;
}
};
var person = new Person('l',1,{
name:'北京',
code:100
});
var person2 = new Person('l',1,{
name:'北京',
code:1000
});
console.log(person.getName === person2.getName);//这里发现地址不同,所以每次创建一个对象就会占用一定空间,浪费资源!
// 原型链的对象创建方式
function Person(){
Person.prototype.name = 'a';
Person.prototype.age = 1;
Person.prototype.address = {
name:'广东',
code:000
}
Person.prototype.getName = function(){
return this.name;
}
}
var person = new Person();
var person2 = new Person();
console.log(person.getName === person2.getName);
// 构造模式和原型模式的混合模式 !!!重要
function Person(name,age,address){
this.name = name;
this.age = age;
this.address = address;
}
Person.prototype.getName = function(){
return this.name;
}
// 生成实例
var person = new Person('K',6,{
name:'重庆',
code:0000
});
var person2 = new Person('K',22,{
name:'重庆',
code:0000
});
console.log(person.getName === person2.getName);
// 动态原型模式 :相当于懒汉模式 !!!重要
function Person(name,age,address){
this.name = name;
this.age = age;
this.address = address;
if(typeof Person._initialized === 'undefined'){
Person.prototype.getName = function(){
return this.name;
}
Person._initialized = true;
}
};
// 生成实例
var person = new Person('K',6,{
name:'重庆',
code:0000
});
var person2 = new Person('K',22,{
name:'重庆',
code:0000
});
console.log(person.getName === person2.getName);
</script>
</body>
</html>
26、对象克隆
<!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>
</head>
<body>
<script>
// 对象克隆
var a = [1,2,3]; // 引用地址 改变一个就全部改变
var b = a;
b[0] = 4;
console.log(b);
console.log(a);
var a = 1; // 基本数据类型 改变一个不会影响
var b = a;
b = 2;
console.log(a);
console.log(b);
// 浅克隆 -- 引用复制
var origin = {
a:1,
b:[2,3,4],
c:{
d:'name'
}
};
function shallowClone(origin){
var result = {};
// 遍历最外层属性
for (var key in origin) {
if (origin.hasOwnProperty(key)) {
result[key] = origin[key];
}
}
return result;
}
console.log(shallowClone(origin));
// var result = shallowClone(origin);
// 通过Object.assign()函数克隆对象
var result = Object.assign({},origin);
console.log(shallowClone(origin));
// -----------------------
// 深克隆
// 第一种JSON序列化 和 反序列化
var origin = {
a:1,
b:[2,3,4],
c:{
d:'name'
}
};
var result = JSON.stringify(origin);
var result2 = JSON.parse(result);
console.log(origin);
console.log(result2);
result2.b[1] = 90;
console.log(origin);
console.log(result2);
/* 缺点:
1、无法实现对函数,RegExp等特殊对象的。
2、对象的constructor会被抛弃,所有的构造函数会指向Object,原型链关系会破裂。
3、对象中如果存在循环引用,会抛出异常。
*/
// 自定义实现深克隆
// 在自定义实现深克隆的时,需啊哟针对不同的数据类型做针对性的处理,因此我们会先实现判断数据类型的函数,
// 并将所有函数封装在一个辅助类中,这里用'_'表示,(类似于underscore类库对外暴露的对象).
// 模拟其他类库对外暴露接口的方式 _
(function(_){
// 列出所以数据类型
var types = 'Array Object String Number Date RegExp Number Boolean Null Undefined'.split(' ');
// console.log(types);
function type(){
// 模拟js中的isArray()方法
}
for(var i=0;i<types.length;i++){
_['is'+types[i]] = (function(){
console.log(_);
return function(elem){
return type.call(elem);
}
})(types[i]);
}
return _;
})(_={});
// 扩展:$.clone() 与 $.extend()
</script>
</body>
</html>
27、原型对象
<!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>
</head>
<body>
<script>
// new操作符调用构造函数创建一个实例时,就会有一个__proto__属性,执行构造函数的原型对象,因此可以把__proto__看做是连接实例与构造函数的原型对象的桥梁!
// 读取顺序:先在实例本身去找,如果没找到,再去原型对象找,找到就返回!
// function Person(){}
// Person.prototype.name = 'cao teacher';
// Person.prototype.age = 1;
// Person.prototype.job = 'code teacher';
// Person.prototype.sayName = ()=>{
// console.log(this.name);
// };
// var person1 = new Person();
// var person2 = new Person();
// console.log(person1.name);
// 重写原型对象
function Person(){}
// 如果在实例化之前调用就会抛出异常!
Person.prototype = {
// 很重要!如果不加constructor就是重写原型对象!
constructor:Person,
name:'kk',
age:3,
job:'code com',
sayName :function(){
console.log(this.name);
}
};
var person1 = new Person();
console.log(person1);
/*
将一个对象字面量赋给prototype属性的方式实际时重写了原型对象,等同于切断了构造函数和最初原型之间的关系,
因此有一 -点需要注意的是,如果仍然想使用constructor属性做后续处理,则应该在对象字面量中增加一个constructor属性,
指向构造函数本身,否则原型的constructor属性会指向Object类型的构造函数,从而导致constructor属性与构造函数的脱离。
*/
</script>
</body>
</html>
28、_ proto _
<!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>
</head>
<body>
<script>
// 一直向上查找 找到返回,找不到就是undefined
Function.prototype.a = 'a'; //找不到
Object.prototype.b = 'b'; //最后就是Object
function Person(){}
var p = new Person();
console.log('p.a',p.a);
console.log('p.b',p.b);
</script>
</body>
</html>
29、继承
<!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>
</head>
<body>
<script>
function Animal(name){
//属性
this.type = 'animal';
this.name = name || '动物'; //参数的默认值赋值
//方法
this.sleep = function(){
console.log(this.name+"正在睡觉!");
}
}
//在原型链上添加方法和属性
Animal.prototype.age = 5;
Animal.prototype.eat = function(food){
console.log(this.name+'正在吃'+food);
}
// 子类
function Cat(name){
this.name = name;
}
//原型链继承
Cat.prototype = new Animal();
// 很关键的一句,将Cat构造函数指向自身!
Cat.prototype.constructor = Cat;
var cat = new Cat('加菲猫');
console.log(cat.type);
console.log(cat.name);
cat.sleep();
console.log(cat.age);
cat.eat('猫粮');
var animal = new Animal();
console.log(animal.type);
console.log(animal.name);
animal.sleep();
console.log(animal.age);
animal.eat('加肥猫');
/*
缺点:1.如果是引用类型,改变Cat继承就会影响其他实例的属性值!
2.在创建子类实现时,无法向父类的构造函数传递参数
3.无法实现多继承
4.为子类添加原型对象上的属性值和函数时,必须放在new Animal()函数之后。
*/
// ----------------------------
// 构造继承 : 主要思想是子类的构造函数中通过call()函数改变this指向,调用父类的构造函数,从而能将父类的实例属性绑定到子类的this上。
// 父类
function Animal(name,age){
this.name = name;
this.age = age;
//方法
this.sleep = function(){
console.log(this.name+'正在睡觉!');
}
}
function Animal2(sex){
this.sex = sex;
}
Animal.prototype.eat = function(food){
console.log(this.name+'正在吃'+food);
}
function Cat(name,age){
Animal.call(this);
Animal2.call(this);
this.name = name;
this.age = age;
}
// 继承
var c = new Cat('tom',3);
console.log(c.name);
// c.eat(); //原型链上的自身属性和原型链的属性都可以使用
c.sleep(); //改变this指向只能使用自身的属性!
/*
构造继承的优点:
1.可解决子类实例共享父类属性的问题。
cal()函数实际是改变了父类Animal构造函数中this的指向,调用后this指向了子类Cat,相当于父类的type,age和sleep等属性和函数直接绑定到了子类的this中,
成了子类实例的属性和函数,因此生成的子类实例中是各自拥有自己的type, age和sleep属性和函数, 不会互相影响。I
2.创建子类的实例时,可以向父类传递参数
在cal()函数中,我们可以传递参数,这是时候参数是传递给父类的,我们就可以对父类的属性进行设置,同时由子类继承下来。
3.可以实现多继承
缺点:1.实例只是子类的实例,并不是父类的实例
2、只能继承父类实例的属性和函数,并不能继承原型对象上的属性和函数
3.无法复用父类的实例函数
*/
// --------------------------
// 复制继承
function Animal(name,age){
this.name = name;
this.age = age;
this.sleep = function(){
console.log(this.name+'正在睡觉!');
}
}
Animal.prototype.eat = function(food){
console.log(this.name+'正在吃'+food);
}
// 子类
function Cat(name,age){
var animal = new Animal('Tom',12);
for(key in animal){
if(animal.hasOwnProperty(key)){
this[key] = animal[key];
}else{
Cat.prototype.key = animal[key];
}
}
return this.name;
}
var c = new Cat('a',100);
c.eat('jack');
/*
优点:
1.支持多继承
2.能同时继承实例的属性和函数与原型对象上的属性和函数(for..in 然后hasOwnProperty())
3.可以向父类构造函数传递值
缺点:
1、父类的所有属性都要复制,消耗内存
2、实例只是子类的实例,并不是父类的实例
*/
// -------------------------
// 组合继承
function Animal(name,age){
this.name = name;
this.age = age;
this.sleep = function(){
console.log(this.name+'正在睡觉!');
}
}
Animal.prototype.eat = function(food){
console.log(this.name+'正在吃'+food);
}
// 子类
function Cat(name,age){
Animal.call(this);
this.name = name;
this.age = age;
}
// 原型链继承
Cat.prototype = new Animal();
// 绑定到自己身上
Cat.prototype.constructor = Cat;
var cat = new Cat('tom',3);
var animal = new Animal('布偶',4);
console.log(cat.name);
console.log(cat.age);
cat.sleep();
cat.eat('jack');
console.log(cat instanceof Cat);
console.log(cat instanceof Animal);
/*
组合继承优点:
1.既能继承父类实例的属性和函数,又能继承原型对象的属性和函数
2.即是子类的实例,有事父类的实例
3.不存在引用属性共享的问题
4.可以向父类的构造函数中传递参数 ,通过call()
缺点: 会绑定两次 一次call 一次prototype
*/
// 寄生组合继承
function Animal(parentAge){
// 实例属性
this.name = 'Animal';
this.age = 'parentAge';
// 实例函数
this.sleep = function(){
console.log(this.name+'正在睡觉');
};
this.feature = ['fat','thin','tall'];
}
// 原型函数
Animal.prototype.eat = function(food){
console.log(this.name+'正在吃'+food);
}
// 子类
function Cat(name){
Animal.call(this);
this.name = name;
}
(function(){
var Super =function(){};
Super.prototype = Animal.prototype;
Cat.prototype = new Super();
Cat.prototype.constructor = Cat;
})();
// 要先有animal再有cat
var animal = new Animal();
var cat = new Cat('tom');
console.log(cat.name);
console.log(animal.age);
</script>
</body>
</html>
30、Object及其实例和静态函数
<!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>
</head>
<body>
<script>
function Cat(name,age){
// 发现这里是一个空对象
// console.log(this);
var Cat = {}; //但是创建一个空对象后发现是没有的
this.name = name;
this.age = age;
return Cat;
}
console.log(new Cat('miaomi',18));
// function Cat(name,age){
// var cat = {};
// cat.name = name;
// cat.age = age;
// return cat;
// }
// var a = Cat('cao',1);
// console.log(a);
function Cat(name,age){
this.name = name;
this.age = age;
}
var a = new Cat('cao',2);
console.log(a);
/*
var cat = {}
cat.__proto__ = Cat.prototype;
Cat.call(cat);
第一行:创建一个空对象
第二行:将空对象的__proto__属性指向Cat对象的prototype属性。
第三行:将Cat()函数中的this指向cat变量
*/
function Cat(name,age){
this.name = name;
this.age = age;
}
Cat.prototype.sayHi = function(){
console.log('Hi');
}
// 模拟new方法
function New(){
var obj = {};
obj.__proto__ = Cat.prototype;
var res = Cat.apply(obj,arguments);
return typeof res === 'object' ? res : obj;
}
console.log(New('mini',10));
console.log(New('mini',1).sayHi());
</script>
</body>
</html>
31、HtmlCollection
<!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>
</head>
<body>
<div id="ad">
<p>
aaa
</p>
<p>
bbb
</p>
</div>
<form id="main">
<input type="text" id="username">
<input type="text" name="username">
<input type="text" name="password">
</form>
<script>
var ad1 = document.getElementById("ad").children;
var ad2 = document.getElementById("ad").childNodes;
console.log(ad1);
console.log(ad2);
// item() 函数
console.log(ad1.item(0));
/*
namedlrem()函数 :
函数用于返回一个节点,首先通过id属性去匹配,如果没有匹配到则使用name属性去匹配,如果还没有匹配到则返回 null。当出现重复id或者name属性时,只返回第一个匹配到的值。
*/
var main = document.getElementById('main').children;
console.log(main.namedItem('username'));
// NodeList:也有item()函数,与HtmlCollection对象一样!
/*
相同点:
1.都是类数组对象,有length属性,可以通过call函数获取apply函数处理成真正的数组
2.都有item()函数,通过索引定位元素
3.都是实时性的
不同点:
HTMLCollection比NodeList多一个namedlrem()函数
HTMLCollection对象只包含元素的集合,即具有标签名的元素,而NodeList对象是节点的集合,即包含元素,也包含节点!
*/
</script>
</body>
</html>
32、事件流
<!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>
</head>
<body>
<table border="1">
<tbody>
<tr>
<td>这是td元素</td>
</tr>
</tbody>
</table>
<script>
var table = document.querySelector('table');
var tbody = document.querySelector('tbody');
var tr = document.querySelector('tr');
var td = document.querySelector('td');
// 冒泡 :默认false 捕获
table.addEventListener('click',function(){
console.log('table监听到了');
},true)
tbody.addEventListener('click',function(){
console.log('tbody监听到了');
},true)
tr.addEventListener('click',function(){
console.log('tr监听到了');
},true)
td.addEventListener('click',function(){
console.log('td监听到了');
},true)
// 完整的事件流是按照事件捕获阶段>事件目标阶段>事件冒泡阶段一次进行的。如果绑定了捕获事件,则会优先冒泡事件而执行!
</script>
</body>
</html>
33、事件处理程序
<!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>
</head>
<body>
<!-- <div id="ad">
aaa
</div> -->
<div id="wwdiv">
监听点击事件
</div>
<button id="btn"></button>
<script src="EventUtil.js"></script>
<script>
// DOM0级事件处理程序
// var ad = document.querySelector("#ad");
// ad.onclick = function(){
// console.log(1);
// }
// ad.onclick = function(){
// console.log(2);
// }
// DOM2级事件处理程序 可以区分冒泡捕获 不会属性覆盖
// ad.addEventListener('click',function(){
// console.log(1);
// });
// ad.addEventListener('click',function(){
// console.log(2);
// })
// 这是针对IE8即一下版本的解决
// ad.attachEvent("onclick",function(){
// console.log(1);
// })
// ad.attachEvent("onclick",function(){
// console.log(1);
// })
// var ad = document.querySelector('#ad');
// EventUtil.addEventHandler(ad,'click',foo);
// EventUtil.removeEventHandler(ad,'click',foo);
// function foo(){
// console.log(1);
// }
/*
DOM3级事件处理程序是在DOM2级基础上重新定义了事件,而且允许自定义事件,自由事件创建由createEvent()函数创建,返回的对象有一个initCustomEvent()函数。
函数可以接收一下4个参数:
1.type:字符串,触发的时间类型
2.bubble:布尔值,表示时间是否可以冒泡。
3.cancelable:布尔值,表示时间是否可以取消。
4.detail:对象,任意值,保存在event对象的detail属性中。
创建完成的自定义事件可以通过dispatchEvent()函数手动触发,触发自定义事件的和绑定自定义事件的元素为同一个元素
*/
// var wwdiv = document.querySelector('#wwdiv');
// var btn = document.querySelector('#btn');
// // 浏览器是否支持DOM3级事件
// console.log(document.implementation.hasFeature('CustomEvents',3.0));
// 获得元素
var customEvent;
// 创建自定义事件
(function(){
if(document.implementation.hasFeature('CustomEvents','3.0')){
var detailData = {name:'kk'};
customEvent = document.createEvent('customEvent');
customEvent.initCustomEvent('myEvent',true,false,detailData);
}
})();
// 获取元素
var div = document.querySelector('#wwdiv');
// 监听myEvent事件
div.addEventListener('myEvent',function(e){
console.log('div监听到自定义执行事件,携带参数为:',e.detail);
});
// 获取元素
var btn = document.querySelector('#btn');
// 绑定click事件,触发自定义事件
btn.addEventListener('click',function(){
div.dispatchEvent(customEvent);
})
</script>
</body>
</html>
EventUtil.js
var EventUtil = {
// 兼容IE和谷歌的DOM2级的方法
addEventHandler:function(element,type,handler){
if(element.addEventListener){
element.addEventListener(type,handler);
}else if(element.attchEvent){
element.attchEvent('on'+type,handler);
}else{
return 'error';
}
},
removeEventHandler:function(element,type,handler){
if(element.addEventListener){
element.removeEventListener(type,handler);
}else if(element.attchEvent){
element.detachEvent('on'+type,handler);
}else{
return 'error';
}
},
// 获取事件对象
getEvent:function(event){
return event || window.event;
},
// 获取事件目标元素
getTarget:function(event){
return event.target || event.srcElement;
}
}
34、_ event对象
<!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>
</head>
<body>
<!-- <button id="btn">
单击
</button> -->
<ul>
<li>
<p>姓名:老师</p>
<p>学号:123456</p>
<button class="btn btn-default" id="btn">删除</button>
</li>
</ul>
<script src="EventUtil.js"></script>
<script>
// var btn = document.getElementById('btn');
// btn.addEventListener('click',function(){
// console.log(event);
// console.log(window.event);
// // 在IE浏览器中,event对象使用srcElement 非IE使用target表示时间的目标元素,某些非IE也支持srcElement
// var ieEvent = event.srcElement;
// var notieEvent = event.target;
// console.log(ieEvent);
// console.log(notieEvent);
// })
// var btn = document.getElementById('btn');
// btn.addEventListener('click',function(event){
// // 不需要穿参数
// console.log(EventUtil.getEvent());
// // 需要穿参数
// console.log(EventUtil.getTarget(event));
// })
var li = document.querySelector('li');
var btn = document.querySelector('#btn');
li.addEventListener('click',function(){
console.log('li监听到了事件!');
});
btn.addEventListener('click',function(){
console.log('button1监听到了事件!');
// 阻止事件冒泡
event.stopPropagation();
})
btn.addEventListener('click',function(event){
console.log('button2监听到了事件!');
// 阻止事件冒泡还可以让其他相同事件不监听,从此刻向下所有!
var event = EventUtil.getEvent(event);
event.stopImmediatePropagation();
})
btn.addEventListener('click',function(){
console.log('button3监听到了事件!');
// 阻止事件冒泡
event.stopPropagation();
})
</script>
</body>
</html>
35、阻止默认行为
<!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>
</head>
<body>
<input type="text" id="text">
<div id="tip">
</div>
<script src="EventUtil.js"></script>
<script>
var oText = document.querySelector('#text');
var oTip = document.querySelector('#tip');
// oText.addEventListener('keypress',function(event){
// // 不同浏览器的兼容键盘码的获取方式
// var charcode = event.keyCode || event.which || event.charCode;
// console.log(charcode);
// })
// 不同浏览器的DOM2级事件处理
EventUtil.addEventHandler(oText,'keypress',function(){
// 不同浏览器的兼容键盘码的获取方式
var charcode = event.keyCode || event.which || event.charCode;
var shuzi = charcode >= 48 && charcode<= 57;
var lower = charcode >= 97 && charcode<= 122;
var upper = charcode >= 65 && charcode<= 90;
if(!shuzi && !lower && !upper){
// 禁止用户输入 阻止默认行为
event.preventDefault();
oTip.innerHTML = '禁止输入除了字母和数字以外的其他字符!';
}
setTimeout(() => {
oTip.innerHTML = '';
}, 2000);
})
</script>
<!--
数字的Unicode48-57
小写字符a-z的Unicode97-122
大写字符的A-Z的Unicode65-90
-->
</body>
</html>
36、事件委托
<!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>
</head>
<body>
<!-- <ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul> -->
<!-- <div id="box">
<input type="button" id="add" value="新增">
<input type="button" id="remove" value="删除">
<input type="button" id="update" value="修改">
<input type="button" id="search" value="查询">
</div> -->
<ul>
<li>文本1</li>
<li>文本2</li>
<li>文本3</li>
<li>文本4</li>
<li>文本5</li>
<li>文本6</li>
<li>文本7</li>
<li>文本8</li>
<li>文本9</li>
</ul>
<button id="add">新增</button>
<script src="EventUtil.js"></script>
<script>
// 获得ul的父类元素
// var oli = document.querySelectorAll('li');
// for(var i=0;i<oli.length;i++){
// oli[i].onclick = function(){
// console.log(this.innerText);
// }
// }
/*
有两个问题:1.事件处理程序过多页面交互时间过长 2.事件处理程序过多导致内存占用过多
*/
/*
事件委托机制的主要思想是将事件绑定到父元素上,然后利用事件冒泡原理,当事件进入冒泡阶段时,
通过绑定在父元素上的事件对象判断当前事件流正在进行的元素,如果和期望的元素相同,则执行相应的事件代码。
*/
// var oUl = document.querySelector('ul');
// oUl.addEventListener('click',function(event){
// // 获取事件对象
// var event = EventUtil.getEvent(event);
// // 获取target
// var target = EventUtil.getTarget(event);
// console.log(target);
// // console.log(target.nodeName);
// // 判断当前事件流所在的元素
// if(target.nodeName.toLowerCase() === 'li'){
// console.log(target.innerText);
// }
// })
// 获取节点
// var box = document.querySelector('#box');
// var add = document.querySelector('#add');
// var remove = document.querySelector('#remove');
// var update = document.querySelector('#update');
// var search = document.querySelector('#search');
// // 为每一个节点添加事件处理程序 ,就是事件委托!
// box.addEventListener('click',function(event){
// // 获取事件对象
// var event = EventUtil.getEvent(event);
// // 获取target
// var target = EventUtil.getTarget(event);
// console.log(target.id);
// switch(target.id){
// case add:
// // add();
// console.log('add');
// break;
// case remove:
// // remove();
// console.log('remove')
// break;
// case update:
// // update();
// console.log('update')
// break;
// case search:
// // search();
// console.log('search')
// break;
// }
// })
// 获得ul的父元素
var oUl = document.querySelector('ul');
oUl.addEventListener('click',function(event){
// 获取事件对象
var event = EventUtil.getEvent(event);
// 获取target
var target = EventUtil.getTarget(event);
console.log(target);
// console.log(target.nodeName);
// 判断当前事件流所在的元素
if(target.nodeName.toLowerCase() === 'li'){
console.log(target.innerText);
}
})
var add = document.querySelector('#add');
add.addEventListener('click',function(event){
var newLi = document.createElement('li');
var newText = document.createTextNode('文本10');
newLi.appendChild(newText);
oUl.appendChild(newLi);
})
</script>
</body>
</html>
37、浏览器的重排与重绘
<!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>
#ad{
width: 100px;
height: 100px;
background-color: red;
}
</style>
</head>
<body>
<div id="ad"></div>
<script>
// 浏览器渲染页面默认采用的是流式的布局模型
var ad = document.querySelector('#ad');
// 将所有的html的引起重排和重绘的代码放在css里面执行
// // 重排
// ad.style.width = '200px';
// ad.style.height = '200px';
// // 重绘
// ad.style.backgroundColor = 'yello';
// 将所有的重排重绘操作放在变量里面去操作
var table = document.createElement('table');
var tr = document.createElement('tr');
var td = document.createElement('td');
// 添加文字信息
// 添加样式
var ta ;
ad.appendChild(ta);
// 将频繁获取会已你去重排的属性缓存到变量
var offsetLeft = ad.offsetLeft;
/*
将需要多次重排的元素设置为绝对定位
将所有的重排重绘操作放在变量里面去操作
将要进行复杂处理的元素处理为display属性为none处理完成后进行显示
将频繁获取会已你去重排的属性缓存到变量
尽量少使用table布局
使用事件委托事件处理程序
*/
</script>
</body>
</html>
38、Ajax执行原理与过程
<!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>
</head>
<body>
<!--
XMLHttpRequest对象的函数:
1. abort)函数:如果请求已经发送,则停止当前请求。
2. getAllResponseHeaders()函数:获取所有HTTP请求的响应头部,作为键值返回,如果没有收到响应,则返回null.
3. getResponseHeader("key")函数:获取指定key的HTTP响应头,如果没有收到响应或者响应中不存在key对象的报头,则返回null.
4. open"method","URL',[asyncFlag]."userName"I.["password")函数:
建立对服务器的调用。
。method参数表示请求方式,可以为GET, POST或者PUT。
。URL参数表示请求的路径, 可以使相对路径,也可以时绝对路径。
。后面3个是可选参数,分别表示是否异步,用户名,密码,其中asyncFlag = true表示异步, asyncFlag =false表示同步,默认值为true。
5. send(content)函数:向服务器发送请求。
6. setRequestHeader(key',value")函数:设置请求头中属性为key的值为value,在设置请求头之前需要先调用open()函数,设置header将随着send()函数- 起发送。
XMLHttpRequest对象的属性:
1. onreadystatechange
状态改变的事件触发器,每个状态改变时都会触发这个事件处理器,通常会调用一个JavaScript函数。
2. readyState
请求的的状态,有5个可取的值。
0:未初始化,XMLHttpRequest对象已经创建。
1: open(函数以调用,send()函数未调用,请求还未发送。
2: send(函数以调用,HTTP请求已经发送到服务器,未接受到响应。
3:所有响应头接受完成,响应体开始接受但未完成。
4: HTTP响应接收完成。
3. reponseText I
接收的数据文本格式的服务器响应体(不包括响应头)
4. responseXML
服务器的响应,兼容DOM的XML对象,解析后可得到DOM对象。
5. status
服务器返回的HTTP状态码,用数字表示,如200表示成功,404表示资源未找到。
6. statusText
HTTP状态码的文本表示,如状态码为200时,对应返回ok,状态码404时对应返回not found。
-->
<script>
// 创建XML
function createXMLHttp(){
//code for IE7 Firefox Chrome Opera safariif
if(window. XMLHttpRequest){
xmlhttp = new XMLHttpRequest();
}
//code for IE6 IES
if (window. Activexobject){
try{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){
try{
xmlhttp = new ActiveXObject("msxm12.XMLHTTP");
}
catch(ex){}
}
}
return xmlhttp;
}
var xhr = createXMLHttp();
/*
建立连接: var xhr = createXMLHttp();
xhr.open('post','/admin/xx',true);
*/
/*
发送请求: var content = {username:'kk',password:'123456'};
xhr.send(content);
*/
/*
处理响应:
在XMLHttpRequest对象中有一个很重要得onreadystatechange属性, 它表示XMLHttpRequest对象状态改变的事件触发器,每次readyState的取值变化时, 属性onreadystatechange对应的函数都会被执行 -次。
当readystate的值为4时代表响应接受完成,需要注意的是响应接收完成并不代表请求是成功的,我们需要通过HTTP请求status状态码判断,当status值为200时代表请求成功。
因此在onreadystatechange()回调函数中,我们需要同时判断readyState和status两个值才 能对响应值做正确的处理。
xhr.onreadystatechange = function(){
if(xhr.readState ===4 && xhr.staus === 200){
document.write(xhr.responseText);
}
}
*/
/*
优点:
1.无刷新更新数据 2.异步通信 3.前后端分离 4.标准化支持
缺点:
1.破坏浏览器的正常后退功能 2.安全性问题 3.对搜索引擎不好 4.违背URL唯一资源定位的初衷
*/
</script>
</body>
</html>
39、Nodejs简单搭建
# 首先下载node.js
创建一个文件夹,这里叫ajaxTest,然后在文件夹里再打开终端
初始化 : npm init 直接回车
安装Express框架和body-parser: npm install experss --save-dev
# 安装方式不同
npm install moduleName -安装到模块项目目录下
npm install -g moduleName -g就是安装到全局,要看具体位置就 npm config prefix
npm install -save modouleName # -save的意思就是将模块安装到项目目录下,并在package文件的dependencies节点写入依赖
npm install -save-dev modouleNmae # -save-dev 的意思是将模块安装到项目目录下,并在package文件的devDependencies节点写入依赖
# 在处理post请求时,需要body-parser插件
所以使用:npminstall body-parser --save-dev
# 创建server.js文件
var express = require('express');
//接收post请求体数据的插件
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser());
// 接收"/"请求,指定首页
app.get('/',function(req,res){
res.sendFile(_dirname+'/index.html');
})
// 处理get请求
app.get('/',function(req,res){
console.log(req,query);
})
// 处理post请求
app.post('/saveUser',function(req,res){
var responseObj = {
code:200,
message:'请求成功'
};
res.write(JSON.stringify(responseObj));
res.end('end');
})
var server = app.listen(3000,function(){})
# 也可以自己编写一个访问地址 index.html
!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>
</head>
<body>
hello ajax
</body>
</html>
# 使用 node server.js 测试
40、Ajax提交form表单
<!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>
</head>
<body>
<form name="userForm" id="userForm">
用户名:<input type="text" name="username" id="username"><br/>
密码<input type="password" name="password" id="password"><br/>
电话:<input type="text" name="telphone" id="telphone"><br/>
邮箱:<input type="text" name="email" id="email"><br/>
<input type="button" value="提交" id="submit">
<input type="button" value="取消" id="cancel">
</form>
<script>
/*
通用处理:1.将form标签的action属性和method属性去掉
2.将提交form表单按钮的type=submit 改为type=button
提交流程:1.绑定提交按钮事件 2.创建XMLHtppRequest对象 3.建立连接 4.设置请求头 5.获取数据 6.发送请求 7.处理响应
*/
// 绑定提交按钮事件
// 在单击提交按钮时,触发Ajax请求操作,将整个Ajax操作封装在ajaxSubmitForm()函数里,按钮获取与事件绑定的原生JavaScript语法
var submit = document.querySelector('#submit');
submit.addEventListener('click',function(){
ajaxSubmitForm()
})
function ajaxSubmitForm(){
console.log(1);
}
// 创建XMLHttpRequest对象
function createxMLHttp(){
//code for IE7 Firefox Chrome Opera safariif
if(window. XMLHttpRequest){
xm1http = new XMLHttpRequest();
}
//code for IE6 IES
if (window. Activexobject){
try{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){
try{
xmlhttp = new ActiveXObject("msxm12.XMLHTTP");
}
catch(ex){}
}
}
}
// 建立连接
xhr.open('post','/saveUser',true);
// 设置请求头
xhr.sendRequestHeader('Content-type','application/json;charset=UTF-8');
// 获取用户输入的数据
</script>
</body>
</html>
41、Ajax的post与get
<!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>
</head>
<body>
<!--
1.参数传递
get请求会将参数添加到请求URL的后面,没有请求主体,调用send()函数时, 传递的参数为null, 即
xhr.send(), post请求的数据会放请求体中,用户是无法通过URL直接看到的,调用send()函数时, 传递的参数为data,即xhr.send(data)。
2.服务端参数获取。
使用Express作为服务端框架,get请求通过Request.query来获取参数, 而使用post请求时需要添加中间件,同时通过Request.body来获取参数。
3.传递的数据量。
get请求传输的数据量小,对于不同的浏览器有所差异,Chrome浏览器限制为8K,E限制为2K,post 请求传递的数据量大,- -般默认不受限制,但实际上服务器会规定post请求传递的数据量大小。
4.安全性。
get请求安全性较低,因为其请求的参数会出现在URL上,而且采用明文进行数据传输。通过浏览器缓存或者历史记录可以很容易获取到某些隐私请求的参数,post请求通过请求体进行数据传输,数据不会出现在URL上,隐藏了请求数据的信息,安全性较高。
5.处理form表单的差异性。
在使用form表单进行提交时,get 请求和post请求也会体现出很大的差异性,我们以下面这段form表单代码为例进行讲解。
-->
<form name="userForm" method="post" action="/getUser?param=面试厅" class="contauner">
用户名:<input type="text" name="username" id="username" class="username"><br>
密码:<input type="text" name="password" id="password" class="password"><br>
<input type="submit" value="提交" id="submit">
</form>
<!--
使用get方式请求 :可能会出现缓存,因此在url后面拼接一个时间戳,避免出现缓存!
使用get请求时,在参数后会拼接在url后,设置编码:通过encodeURLComponent()函数
在浏览器、服务器、数据库、url编码检验!!!
xhr.open('get','/getUser?username'+encodeURLComponent(username),true);
-->
<!--
在之前的内容里,我们有讲到通过监听readystatechange事件,在回调函数中获取readyState 和status的值并判断请求是否成功。在XHR2草案中,增加了Ajax请求进度事件Progress Events规范,使得
XMLHttpRequest对象能在请求的不同阶段触发不同类型的事件,所以我们可以不再需要判断readyState的属性,也可以处理请求成功和失败的操作。
在Progress Events规范中增加了7个进度事件,如下所示。
1. loadstart:在开始接收响应时触发。|
2. progress:在接收响应期间不断触发,直至请求完成。
3. error. 在请求失败时触发。
4. abort:在主动调用abort()函数时触发,表示请求终止。
5. load: 在数据接收完成时触发。
6. loadend:在通信完成或者error、abort. load事件后触发。
7. timeout: 在请求超时时触发。
-个完整的ajax请求都会从loadstart事件开始,然后不间断地触发progress事件,然后触发load、abort, S中,8目gtimeout或者error事件中的一个,
注意这里是只会触发load、abort, timeout或者error事件其中的一一个,最后触发loadend事件。
这些事件大都很直观,通过对它们的描述就可以很好地理解,但是load 事件和progress事件有些细节需要注意,接下来具体讲解。
-->
</body>
</html>
42、Ajax进度事件
<!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>
</head>
<body>
<!--
load事件的诞生是用以代替readystatechange事件的,表示的是数据接收完成后触发。我们不用去判断readyState属性值的变化也可以执行事件处理成功的操作。
但是有一点需要注意的是,只要浏览器接收到了服务器的响应,不管其状态如何都会触发load事件。
例如,对于状态码为404的请求,仍然会触发load事件,所以在进行请求成功的处理时,需要判断status的值。一 般我们判断status值大于等于200且小于300,或者status值等子 304时,都是当作请求成功进行处理。
在loadstart, load等事件的回调函数中,都会接收一个event对象,通过event对象的target属性可以获取到XMLHttpRequest对象的实例,因此可以访问到XMLHttpRequest对象的所有属性和函数。
-->
<script>
// 创建XML
function createXMLHttp(){
//code for IE7 Firefox Chrome Opera safariif
if(window. XMLHttpRequest){
xmlhttp = new XMLHttpRequest();
}
//code for IE6 IES
if (window. Activexobject){
try{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){
try{
xmlhttp = new ActiveXObject("msxm12.XMLHTTP");
}
catch(ex){}
}
}
return xmlhttp;
}
var xhr = createXMLHttp();
// 设置onloadStart事件监听
xhr.onloadStart = function(event){
console.log('开始事件监听')
}
// 设置onerror事件异常
xhr.onerror = function(event){
console.log('事件异常')
}
// 设置ontimeout事件超时
xhr.ontimeout = function(event){
console.log('事件超时')
}
// 方式一获取status
var status = xhr.status;
// 方式二获取status
var status = event.target.status;
console.log('load事件状态码:'+status);
if(status >= 200 && status <300 || status ===304){
console.log('load事件-数据传输成功!');
}
</script>
</body>
</html>
43、JSON序列化与反序列化
<!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>
</head>
<body>
<script>
// JSON 不能处理函数
// JSON.stringify(value[replacer[,space]]);
/*
1. value一般为对象或者数组 ,如果是字符串就先转换!
2. replacer参数是一个可选参数, 如果其值为一个函数,则表示在序列化过程中,被序列化值的每个属性,都会经过该函数的处理,如果其值是一个数组,
则表示只有包含在这个数组中的属性名才会被序列化到最终的JSON字符串中,如果该值为null或者未传递,则value参 数对应值的所有属性都会被序列化。
3. space是一个可选参数, 用于指定缩进用的空白字符串,美化输出,如果参数是个数字,则代表有多少个空格,上限值为10,如果该参数的值小于1,则意味着没有空格,
如果参数为字符串,则取字符串的前十个字符作为空格,如果没有传入参数或者传入的值为null,将没有空格。
*/
var obj = {
name:'kk',
age:3,
address:String('上海'),
interest:['basketball','football'],
email:'zhaoqq.com'
}
// console.log(JSON.stringify(obj));
function toJsonUppercase(key,value){
if(typeof value === 'string'){
return value.toLocaleUpperCase();
}
return value;
}
// console.log(JSON.stringify(obj,toJsonUppercase));
// 在值得传递的时候 指定序列化的key
console.log(JSON.stringify(obj,['name','age']));
// 如果属性值为对象或者数组,则会继续序列化直到吃呢各位基本数据类型、函数或者Symbol结束!
// 序列化数组为多种包装类型的代码
console.log(JSON.stringify[new Number(1),new String('false'),new Boolean('false')])
// console.log(JSON.stringify({x:undefined,y:Object}));
// ----------------------
// 定义toJson()函数
// var obj = {
// name:'kk',
// age:3,
// address:String('上海'),
// interest:['basketball','football'],
// email:'zhaoqq.com',
// toJson:function (){
// //只返回name ,age ,并且修改key
// return{
// Name:this.name,
// Age:this.age
// }
// }
// };
// console.log(JSON.stringify(obj));
// ------------
// JSON反序列化
// let arr = [1,2,3,4];
// var arrJson = JSON.stringify(arr);
// console.log(JSON.parse(arrJson));
// 格式问题要用双引号包裹
let jsonStr = '{"name":"kirito","age":1}';
var result = JSON.parse(jsonStr,function(key,value){
if(key === 'name'){
return value + '同学';
}
if(key === 'age'){
return value * 2;
}
return value;
})
console.log(result);
var json = '{"name":"cc"}';
// 两个功能相似 eval与parse
var json2 = eval("("+json+")");
console.log(json2);
</script>
</body>
</html>
44、CORS、JSONP
<!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>
</head>
<body>
<form name="userForm">
学号:<input type="text" id="studentNo" name="studentNo">
搜索:<input type="button" value="搜索" id="btn">
</form>
<script>
/* CORS
nodejs 跨域问题解决:
在response-server.js 假如下面代码:
app.all("*",function(req,res,next){
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin","*");
//允许的header类型
res.header("Access-Control-Allow-Headers","content-type");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); //让options尝试请求快速结束
else
next();
}
/*
JSONP的主要思想可以分两步理解:
在网页中动态添加一个script标签, 通过script标签向服务器发送请求, 在请求中会携带一个请求的Callback回调函数名。
服务器在接收到请求后,会处理响应获取返回的参数,然后将参数放在callback回调函数中对应的位置,并将callback回调函数通过json格式进行返回。
*/
/*
JSONP : 不能处理post
*/
var btn = document.getElementById('btn');
btn.onclick = function(){
sendRequest();
}
function sendRequest(){
var studentNo = document.getElementById('studentNo').value;
// 发送到对应的3000请求
// 请求参数,其中包括回调函数
var param = 'studentNo='+studentNo+'&callback=successFn';
// 请求的url
var url = 'http://localhost:3000/getUserByStudentNo?'+param;
// 动态创建标签
var script = document.createElement('script');
// 请求地址设置
script.src = url;
// 将script标签放在body中
document.body.appendChild(script);
}
// 接收的回调函数
var successFn = function(result){
console.log(result);
}
</script>
</body>
</html>