js高级第五天
课程回顾:
原型链:由原型构成链状结构,提供成员查找机制
继承:组合继承:构造函数和原型对象
属性:调用父构造函数的时候用call改变this指向
方法:父实例对象赋值给子原型对象,最后指回构造函数本身
新增方法:
数组:forEach,filter,some
字符串:trim
语法:数组.方法名(function ( val, index, obj ) { });
ES6中的新增方法
ES6 中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:
数组方法
字符串方法
数组方法:
迭代(遍历)方法:forEach()、map()、filter()、some()、every();
这些方法都是遍历数组的
forEach()
array.forEach(function(currentValue, index, arr))遍历数组
currentValue:数组当前项的值
index:数组当前项的索引
arr:数组对象本身
var arr = ['red','blue','yellow','orange'];
arr.forEach(function (elm,i,arrAbc) {
console.log(elm,i,arrAbc);
});
filter()
array.filter(function(currentValue, index, arr))
filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用于筛选数组
注意它直接返回一个新数组
currentValue: 数组当前项的值
index:数组当前项的索引
arr:数组对象本身回调函数里面添加return添加返回条件
var arr = [100,66,99,123,333,33,44,66];
var reArr = arr.filter(function (elm, a, n) {
// console.log(elm,a, n);
return elm % 2 == 0;
});
console.log(reArr);
some()
array.some(function(currentValue, index, arr)) 【注意:找到或者满足条件立刻停止】
some() 方法用于检测数组中的元素是否满足指定条件. 通俗点查找数组中是否有满足条件的元素
注意它返回值是布尔值, 如果查找到这个元素, 就返回true , 如果查找不到就返回false.
如果找到第一个满足条件的元素,则终止循环. 不在继续查找.
currentValue: 数组当前项的值index:数组当前项的索引
arr:数组对象本身
var arr = [100,200,300,400];
var re = arr.some(function (elm,i,arr) {
// console.log(elm,i,arr);
console.log(i);
return elm >= 200;
});
console.log(re);
查询商品案例
1.把数据渲染到页面中(forEach)【insertAdjacentHTML】
var tbody = document.querySelector('tbody');
data.forEach(function (ele, i) {
// console.log(ele);
var tr = '<tr><td>' + ele.id +'</td><td>' + ele.pname + '</td><td>' + ele.price + '</td></tr>';
tbody.insertAdjacentHTML('beforeend',tr);
});
2.根据价格显示数据
var btn = document.querySelector('.search-price');
var start = document.querySelector('.start');
var end = document.querySelector('.end');
btn.onclick = function () {
var reArr = data.filter(function (ele, i) {
return start.value <= ele.price && ele.price <= end.value;
});
tbody.innerHTML = '';s
reArr.forEach(function (ele) {
var tr = '<tr><td>' + ele.id +'</td><td>' + ele.pname + '</td><td>' + ele.price + '</td></tr>';
tbody.insertAdjacentHTML('beforeend',tr);
});
};
3.根据商品名称显示数据
var sele = document.getElementById('sele');
sele.onchange = function () {
var n = [];
var id = sele.value;
data.some(function (ele) {
if (id == 0) {
n = data;
return true;
}else if (ele.id == id) {
n.push(ele);
return true;
}
});
tbody.innerHTML = '';
n.forEach(function (ele, i) {
// console.log(ele);
var tr = '<tr><td>' + ele.id +'</td><td>' + ele.pname + '</td><td>' + ele.price + '</td></tr>';
tbody.insertAdjacentHTML('beforeend',tr);
});
}
必做题:求这个是数组所有数的和,求数组最大值和最小值,及最大值的下标和最小值的下标,求平均值;
var arr = [[123,45,66],[1234,23,45,56,67],[435,67,78,43,67,78,673,34]]
字符串方法:
str.trim()
trim:删除字符串两侧的空白符
<input type="text" id="txt">
<input type="button" value="点击" id="btn">
<script type="text/javascript">
// var str = new String('abcd');
// var str = ' abc defg abcd ';
// console.log( str );
// console.log( str.trim() );
var btn = document.getElementById('btn');
var txt = document.getElementById('txt');
btn.onclick = function () {
if ( txt.value.trim().length > 0 ) {
alert('允许提交');
} else {
alert('不允许提交');
}
}
</script>
函数进阶
函数的定义和调用
-
函数声明方式function 关键字(命名函数)
-
函数表达式(匿名函数)【自调用函数】
-
new Function() var fn = new Function(‘参数1’,‘参数2’…, ‘函数体’)
var fn = new Function('a','b','console.log(a,b);'); fn(123,456); Function 里面参数都必须是字符串格式 第三种方式执行效率低,也不方便书写,因此较少使用 所有函数都是Function 的实例(对象) 函数也属于对象 // var n = 3; // var fn = function () {}
函数的调用方式
函数定义:命名函数,匿名函数
调用函数:好多种
- 普通函数
- 对象的方法
- 构造函数
- 绑定事件函数
- 定时器函数
- 立即执行函数
// 普通函数
// function fn () {
// console.log(123);
// }
// fn();
// 对象方法
// var obj = {
// name : '张三丰',
// taiji : function () {
// console.log(123);
// }
// }
// // console.log(obj.taiji());
// obj.taiji();
// 构造函数
// function Fun (uname, age) {
// this.uname = uname;
// this.age = age;
// }
// new Fun('李寻欢', 23);
// 事件处理程序
// btn.onclick = function () {
// console.log(123);
// }
// btn.onclick();
// 定时器
// window.setInterval(function () {
// console.log('哇哈哈');
// }, 1000);
// 立即执行函数
(function () {
console.log(123);
})();
this指向
this指向谁,如果大家记不住,那么就可以这样来记:
构造函数中的this指向的是实例对象
其他的都是调用者
改变函数内部this 指向
JavaScript 为我们专门提供了一些函数方法来帮我们更优雅的处理函数内部this的指向问题,常用的有bind()、call()、apply() 三种方法。
call 方法
call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的this指向。
fun.call(thisArg, arg1, arg2, ...)
thisArg:在fun 函数运行时指定的this 值
arg1,arg2:传递的其他参数
返回值就是函数的返回值,因为它就是调用函数
function Father () {this}
function Son () { Father.call(this,1,2) }
因此当我们想改变this 指向,同时想调用这个函数的时候,可以使用call,比如继承
var obj = {
name : '阿飞',
fei : function () {
console.log(this);
}
}
var o = {name : '带土'};
// 函数.call(对象)
// console.log( obj.fei );
obj.fei.call(o);
apply 方法
fun.apply(thisArg, [argsArray]):调用函数
thisArg:在fun函数运行时指定的this值
argsArray:传递的值,必须包含在数组里面
返回值就是函数的返回值,因为它就是调用函数
因此apply 主要跟数组有关系,比如使用Math.max() 求数组的最大值
var arr = [23,45,56,23,54];
var n = Math.max.apply(null,arr);
console.log(n);
改变this指向:fn.apply(obj,[数组])
fn.call(obj,a,b,c,d)
bind 方法
bind() 方法不会调用函数。但是能改变函数内部this 指向
fun.bind(thisArg, arg1, arg2, ...)
thisArg:在fun 函数运行时指定的this 值
arg1,arg2:传递的其他参数
返回由指定的this 值和初始化参数改造的原函数拷贝
因此当我们只是想改变this 指向,并且不想调用这个函数的时候,可以使用bind
var btn = document.querySelector('input');
btn.onclick = function () {
this.disabled = true;
window.setTimeout(function () {
this.disabled = false;
}.bind(btn),2000);
}
call apply bind 总结
fun.call(obj,arg1,arg2......);
fun.apply(obj,[a,b,c])
fun.bind(obj,arg1,arg2......);
相同点: 都可以改变函数内部的this指向.
区别点:
1.call 和apply 会调用函数, 并且改变函数内部this指向.
2.call 和apply 传递的参数不一样, call 传递参数aru1, aru2..形式apply 必须数组形式[arg]
3.bind 不会调用函数, 可以改变函数内部this指向
主要应用场景:
1.call 经常做继承.
2.apply 经常跟数组有关系.比如借助于数学对象实现数组最大值最小值
3.bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向
严格模式
JS:两种模式[类似于HTML版本]
1、正常模式
2、严格模式
什么是严格模式
JavaScript 除了提供正常模式外,还提供了严格模式(strictmode)。ES5 的严格模式是采用具有限制性JavaScript 变体的一种方式,即在严格的条件下运行JS 代码。
严格模式在IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的JavaScript 语义做了一些更改:
1.消除了Javascript语法的一些不合理、不严谨之处,减少了一些怪异行为。【例如变量,不声明就报错】
2.消除代码运行的一些不安全之处,保证代码运行的安全。
3.提高编译器效率,增加运行速度。
4.禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的Javascript做好铺垫。比如一些保留字如:class, enum, export, extends, import, super 不能做变量名
开启严格模式
开启严格模式:“use strict”
<script>"use strict"</script>:脚本开启严格模式
<script>function fn () {"use strict"}</script>为函数开启严格模式
严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。
为脚本开启严格模式
为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句“use strict”;(或‘use strict’;)。
<script>
"use strict";
console.log("这是严格模式。");
</script>
因为"use strict"加了引号,所以老版本的浏览器会把它当作一行普通字符串而忽略。
为函数开启严格模式
要给某个函数开启严格模式,需要把“use strict”; (或'use strict'; ) 声明放在函数体所有语句之前。
function fn(){"use strict";return "这是严格模式。";}
将"use strict"放在函数体的第一行,则整个函数以"严格模式"运行。
严格模式中的变化
严格模式对Javascript的语法和行为,都做了一些改变。
变量规定
变量申明必须加var,而且不准删除变量
在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,变量都必须先用var命令声明,然后再使用。
n = 3;
严禁删除已经声明变量。例如,delete x; 语法是错误的。
严格模式下this 指向问题
严格模式下,普通函数this是undefined;
以前在全局作用域函数中的this 指向window 对象。
严格模式下全局作用域中函数中的this是undefined。
其他的没有变化:
以前构造函数时不加new也可以调用,当普通函数,this 指向全局对象
严格模式下,如果构造函数不加new调用, this 指向的是undefined 如果给他赋值则会报错
new 实例化的构造函数指向创建的对象实例。
定时器this 还是指向window 。
事件、对象还是指向调用者。
函数变化
参数不能重名
函数不能有重名的参数。
函数必须声明在顶层.新版本的JavaScript 会引入“块级作用域”(ES6 中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数。【if,for等里面定义函数也不可以,但是现在不可以】
更多严格模式要求参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode
错误写法:
function fn (a,a) {console.log(a+a);}
fn(1,2);
JS:正常模式,严格模式
开启严格模式:"use strict";脚本开启,函数开启
变量严格,普通函数this指向undefined,函数参数不能重名......
高阶函数
高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。
把函数作为参数,或者把函数作为返回值的的函数,称为高阶函数
此时fn就是一个高阶函数
函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。
同理函数也可以作为返回值传递回来
<script>
function fn(callback){
callback&&callback();
}
fn(function(){
alert('hi');
})
</script>
<script>
function fn(){
return function() {}
}
fn();
</script>
课程回顾:
函数定义:命名函数,匿名函数,new Function()
调用函数:普通函数,事件处理程序,方法…
this指向:构造函数this指向实例对象,其他this指向调用者
改变this指向:call,apply,bind【fun.方法(对象,参数)】
严格模式:“use strict”;脚本开启严格,函数开启严格
高阶函数:把函数当做参数或者把函数当做返回值的函数,就是高阶函数
课程回顾:
函数定义:命名函数,匿名函数,new Function
函数调用:普通函数,事件处理程序,方法…
this指向:构造函数this指向实例对象,其他的this指向当前调用者
改变this指向:call,apply,bind
严格模式:正常模式,严格模式(“use strict”),整个脚本开启,为函数开启
高阶函数:把函数当做参数或者返回值的函数
闭包
变量作用域
变量根据作用域的不同分为两种:全局变量和局部变量。
函数内部可以使用全局变量。
函数外部不可以使用局部变量。
当函数执行完毕,本作用域内的局部变量会销毁。
什么是闭包
闭包作用:延伸变量的作用范围,以及使用周期
闭包(closure)指有权访问另一个函数作用域中变量的函数。【很多种解释,都并不权威】
【子函数访问父作用域中的局部变量】
<script>
function fn1(){
// fn2 就是闭包函数
var num = 10;
function fn2(){
console.log(num); // 10
}
fn2()
}
fn1();
</script>
思考:如何再函数外面访问到函数内部的变量
function fn () {
var i = 7;
return function () {
console.log(i);
}
// function fn1 () {
// console.log(i);
// }
// fn1();
}
var n = fn();
n();
练习:
注册事件练习:打印索引值
var lis = document.querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
(function (index) {
lis[index].onclick = function () {
console.log(index);
}
})(i);
}
思考题:
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
console.log(object.getNameFunc()())
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var that = this;
return function() {
return that.name;
};
}
};
console.log(object.getNameFunc()())
闭包:有权访问另外一个函数的局部变量的函数,作用:延长变量的使用范围
递归:函数调用其本身,用return;停止函数
递归
什么是递归
递归:**如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数
函数的递归,递归函数
递归:函数调用函数其本身
**注意:**递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。
练习:
利用递归求1~n的任意一个数的阶乘
//利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n
function fn(n) {
if (n == 1) { //结束条件
return 1;
}
return n * fn(n - 1);
}
console.log(fn(3));
利用递归求斐波那契数列
// 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21...
// 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
// 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值
function fb(n) {
if (n === 1 || n === 2) {
return 1;
}
return fb(n - 1) + fb(n - 2);
}
console.log(fb(3));
思考题羊村:50人家,每户一只羊
每户只能看别人家的羊有木有病
每户只能杀自己家的羊
第一天,第二天 ,第三天,砰砰砰几声枪响,问杀了几只羊
利用递归遍历数据
var data = [
{
id : 1,
name : '家电'
},
{
id : 2,
name : '服饰'
}
];
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
gname: '冰箱',
goods: [{
id: 111,
gname: '海尔'
}, {
id: 112,
gname: '美的'
},
]
}, {
id: 12,
gname: '洗衣机'
}]
}, {
id: 2,
name: '服饰'
}];
//1.利用 forEach 去遍历里面的每一个对象
function getID(json, id) {
var o = {};
json.forEach(function(item) {
// console.log(item); // 2个数组元素
if (item.id == id) {
// console.log(item);
o = item;
// 2. 我们想要得里层的数据 11 12 可以利用递归函数
// 里面应该有goods这个数组并且数组的长度不为 0
} else if (item.goods && item.goods.length > 0) {
o = getID(item.goods, id);
}
});
return o;
}
深拷贝和浅拷贝
拷贝不能直接赋值,对象赋值的是地址
var obj = {
name : '张三丰',
age : 22
};
var newObj = obj;
console.log(newObj);
浅拷贝:
只拷贝最外面一层
var obj = {
name : '张三丰',
age : 22
};
var newObj = {};
for (key in obj) {
newObj[key] = obj[key];
}
console.log(newObj);
es6:新方法
Object.assign(target, sources);
console.log(newObj);
深拷贝
<script type="text/javascript">
var obj = {
name : '张三丰',
age : 22,
color : ['red','blue','yellow'],
message : {
sex : '男',
score : 99
}
}
var newObj = {};
// 定义一个函数完成
function kaobei (newObj, obj) {
// newObj新的对象
// obj就是有内存的对象
for (key in obj) {
// newObj[key] = obj[key];
if (obj[key] instanceof Array) { // obj[key]是否是数组
//切记数组与对象的判断时,先判断数组结构,因为万物皆对象,会与下面的对象判断出现错乱
// 数组一定是Array的实例对象
// 继续吧数组遍历
newObj[key] = [];
//利用递归作用调用自己的方式,达到效果
kaobei(newObj[key], obj[key]);
} else if (obj[key] instanceof Object) {// obj[key]是否是是对象
// 继续遍历这个对象
newObj[key] = {};
kaobei(newObj[key], obj[key]);
} else {
newObj[key] = obj[key];
}
}
}
kaobei(newObj, obj);
obj.message.sex = '女';
console.log( obj );
console.log( newObj );
// console.log( typeof [12,3] );
</script>
课程回顾:
闭包:有权访问另外一个函数的局部变量的函数,作用:延伸变量使用范围
function () {
var n = 3;
return function () {
console.log(n);
}
}
递归:函数调用其本身
function fn () {
fn();
}
浅拷贝和深拷贝
console.log(newObj);
es6:新方法
Object.assign(target, sources);
console.log(newObj);
### 深拷贝
var obj = {
name : ‘1张三丰’,
age : 22,
messige : {
sex : ‘男’,
score : 16
},
color : [‘red’,‘purple’,‘qing’]
}
var newObj = {};
function kaobei (newObj,obj) {
for (key in obj) {
if (obj[key] instanceof Array) {
newObj[key] = [];
kaobei(newObj[key],obj[key]);
} else if (obj[key] instanceof Object) {
newObj[key] = {};
kaobei(newObj[key],obj[key])
} else {
newObj[key] = obj[key];
}
}
}
obj.messige.sex = 99;
kaobei(newObj,obj);
console.log(newObj);
课程回顾:
闭包:有权访问另外一个函数的局部变量的函数,作用:延伸变量使用范围
function () {
var n = 3;
return function () {
console.log(n);
}
}
递归:函数调用其本身
function fn () {
fn();
}
浅拷贝和深拷贝