一、函数的定义和调用
1.函数的定义方式
(1) 函数声明方式 function 关键字 (命名函数)
function fn( ){ }
(2)函数表达式(匿名函数)
var fn = function( ){ }
(3)方式3 new Function( )
var fn = new Function('参数1','参数2'..., '函数体')
注意:①Function 里面参数与函数体都必须是字符串格式
②所有函数都是 Function 的实例(对象)
③函数也属于对象
2.函数的6种调用
/* 1. 普通函数 */
function fn() {
console.log('人生的巅峰');
}
fn();
/* 2. 对象的方法 */
var o = {
sayHi: function () {
console.log('人生的巅峰');
},
};
o.sayHi();
/* 3. 构造函数*/
function Star() {}
new Star();
/* 4. 绑定事件函数*/
btn.onclick = function () {}; // 点击了按钮就可以调用这个函数
/* 5. 定时器函数*/
setInterval(function () {}, 1000);// 这个函数是定时器自动1秒钟调用一次
/* 6. 立即执行函数(自调用函数)*/
(function () {
console.log('人生的巅峰');
})();
二、函数的this指向
1.六中函数的this指向
2.call( )方法改变函数内部this的指向
具体见JS高级第二天。构造函数的继承会用到。
3.apply( )方法改变函数内部this的指向
函数名.apply( this的指向,[ ] )
①applay( )方法可以调用函数
②第一个参数代表this的指向
③第二个参数必须是一个数组(伪数组)
④注意apply( )方法的参数数组有几个元素,原函数的参数就要有几个形参接收。
function fn(a,b,c){
console.log(this); // 指向对象o
console.log(a,b,c);
}
o = {name: '吴磊'};
fn.apply(o,[1,2,3]);
- 应用:可以利用数学的内置对象求数组的最值等。
// 利用数学的内置对象求数组的最值
var arr = [12,34,33,64];
var max = Math.max.apply(Math,arr);
console.log(max);
4.bind( )方法
函数.bind( this的指向 , 参数1 , 参数2...)
①bind( )方法不会调用函数,但是可以改变函数的this指向。
②返回的是一个改变this指向的新函数。(了解)
- 应用:改变定时器函数的this指向,并且不会调用这个函数。
var btns = document.querySelectorAll('button');
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', function () {
this.disabled = true; //这个this指向事件绑定对象--当前button按钮
setTimeout(function () {
this.disabled = false; //改变后的this
}.bind(this),2000); //将定时器函数的this指向事件绑定对象--当前button按钮
});
}
三、严格模式
1.开启严格模式
(1)给script标签开启严格模式:在script标签内部的最顶端添加" use strict "
(2)给立即执行函数开启严格模式:在function花括号内部的最顶端添加" use strict "
(3)给单个函数开启严格模式:在函数内部的最顶端添加" use strict "
2.严格模式的主要变化
(1)严格模式下变量必须声明后才能使用。非严格模式下不声明的变量是全局变量。
(2)严格模式下不允许删除变量。
(3)严格模式下全局作用域中的函数this指向underfined。非严格模式下指向window。
function fn() {
console.log(this); // 严格模式下全局作用域中函数中的 this 是 undefined
}
fn();
(4)严格模式下的构造函数必须实例化对象后才能调用,否则this指向underfined会出错。
function Star() {
this.sex = '男';
}
// Star();严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错.
(5)严格模式下函数的参数不能重名。
(6)严格模式下if语句、for循环语句内不能书写函数。
四、闭包
1.作用域
①函数内部可以使用全局变量。
② 函数外部不可以使用局部变量。
③ 当函数执行完毕,本作用域内的局部变量会销毁。
2.什么是闭包
闭包是一个函数,其他函数作用域可以访问闭包函数中的局部变量。
3.闭包的作用
①可以延伸局部变量的作用域(函数外部可以访问内部的局部变量)
②函数执行完毕后,某些局部变量不会立即销毁,因为外部也可以访问局部变量。
③核心原理:父函数内部return返回子函数。
function father() {
var num = 10; //闭包函数里的局部变量
function son() {
console.log(num); //其他函数访问闭包函数的局部变量
}
return son; //将son函数作为返回值
}
var s = father(); //用s接收返回值son()
//这句话相当于:
// var s = function son() {
// console.log(num);
// }
s(); //调用son(),函数外部访问了函数内部的局部变量。
4.闭包的应用
https://zhuanlan.zhihu.com/p/686959137
封装节流函数和防抖函数
/**
* 封装节流函数
* @param {Function} func 需要节流的函数
* @param {Number} delay 节流时间
* @returns {Function} 节流函数
*/
function throttle(func, delay) {
let timer = null;
return function () {
if (!timer) {
timer = setTimeout(() => {
func.apply(this, arguments);
timer = null;
}, delay)
}
}
}
// 页面滚动事件节流
window.addEventListener('scroll', throttle(() => {
console.log("scrolling!");
}, 1000, true))
/**
* 封装防抖函数
* @param {Function} func 需要防抖的函数
* @param {Number} delay 防抖时间
* @returns {Function} 防抖函数
*/
function debounce(func, delay) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func.apply(this, arguments);
}, delay)
}
}
// 输入框输入事件防抖
document.getElementById('input').addEventListener('input', debounce(() => {
console.log("inputing!");
}, 1000))
利用闭包实现给多个li标签绑定点击事件,输出当前li的索引号。
分析:
①由于for循环是同步任务,点击事件是异步任务,当点击事件触发时循环已经结束,索引号i已经变成最大值。
②普通方法中,需要在循环中给每个li标签添加一个自定义属性,属性值为索引。
③使用闭包,每次循环都创建一个立即执行函数,索引号i作为实参传入立即执行函数。立即指向函数内部就可以使用索引i了。
var lis = document.querySelector('ul').children;
for (var i = 0; i < lis.length; i++) {
(function (a) {
lis[a].onclick = function () {
console.log(a);
};
})(i); //索引号i作为实参传入立即执行函数
}
}
五、浅拷贝和深拷贝
1.原生js的浅拷贝====》jQuery的$extends( )
浅拷贝只拷贝数据的第一层,更深层次的对象类型数据只拷贝地址。
Object.assign( 目标拷贝对象,被拷贝对象 )
// 把对象a拷贝给对象b
var a = {
uname: '吴磊',
age: 22,
hobby: {
play:" ball",
},
};
var b = {};
// 1.浅拷贝
// Object.assign(b, a);
// 2.深拷贝
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
var item = oldobj[k]; //把被拷贝对象的每一个属性值赋给item变量
if (item instanceof Array) {
// 判断item是不是数组
newobj[k] = [];
deepCopy(newobj[k], item);
} else if (item instanceof Object) {
// 判断item是不是对象
newobj[k] = {};
deepCopy(newobj[k], item);
} else {
// 简单数据类型
newobj[k] = item;
}
}
}
deepCopy(b, a);
console.log(b);