apply和call方法的使用
作用:可以改变this指向
//apply和call都可以改变this的指向
//函数的调用,改变this的指向
// function f1(x,y) {
// console.log((x+y)+":===>"+this);
// return "这是函数的返回值";
// }
// //apply和call调用
// var r1=f1.apply(null,[1,2]);//此时f1中的this是window
// console.log(r1);
// var r2=f1.call(null,1,2);//此时f1中的this是window
// console.log(r2);
// console.log("=============>");
// //改变this的指向
// var obj={
// sex:"男"
// };
// //本来f1函数是window对象的,但是传入obj之后,f1函数此时就是obj对象的
// var r3=f1.apply(obj,[1,2]);//此时f1中的this是obj
// console.log(r3);
// var r4=f1.call(obj,1,2);//此时f1中的this是obj
// console.log(r4);
//方法改变this的指向
// function Person(age) {
// this.age = age;
// }
// Person.prototype.sayHi = function (x, y) {
// console.log((x + y) + ":====>" + this.age);//是实例对象
// };
//
// function Student(age) {
// this.age = age;
// }
// var per = new Person(10);//实例对象
// var stu = new Student(100);//实例对象
// //sayHi方法是per实例对象的
// per.sayHi.apply(stu, [10, 20]);
// per.sayHi.call(stu, 10, 20);
//apply和call的使用方法
/*
* apply的使用语法
* 函数名字.apply(对象,[参数1,参数2,...]);
* 方法名字.apply(对象,[参数1,参数2,...]);
* call的使用语法
* 函数名字.call(对象,参数1,参数2,...);
* 方法名字.call(对象,参数1,参数2,...);
*
* 作用:改变this的指向
* 不同的地方:参数传递的方式是不一样的
*
* 只要是想使用别的对象的方法,并且希望这个方法是当前对象的,那么就可以使用apply或者是call的方法改变this的指向
*
* */
function f1() {
console.log(this+":====>调用了");
}
//f1是函数,f1也是对象
console.dir(f1);
//对象调用方法,说明,该对象中有这个方法
f1.apply();
f1.call();
console.log(f1.__proto__==Function.prototype);
//所有的函数都是Function的实例对象
console.log(Function.prototype);//ƒ () { [native code] }
console.dir(Function);
//apply和call方法实际上并不在函数这个实例对象中,而是在Function的prototype中
function Person() {
this.sayHi=function () {
console.log("您好");
};
}
Person.prototype.eat=function () {
console.log("吃");
};
var per=new Person();
per.sayHi();
per.eat();
console.dir(per);
//实例对象调用方法,方法要么在实例对象中存在,要么在原型对象中存在
bind方法
function f1(x, y) {
console.log((x + y) + ":=====>" + this.age);
}
//复制了一份的时候,把参数传入到了f1函数中,x===>10,y===>20,null就是this,默认就是window
//bind方法是复制的意思,参数可以在复制的时候传进去,也可以在复制之后调用的时候传入进去
//apply和call是调用的时候改变this指向
//bind方法,是赋值一份的时候,改变了this的指向
// var ff=f1.bind(null);
// ff(10,20);
// function Person() {
// this.age = 1000;
// }
// Person.prototype.eat = function () {
// console.log("这个是吃");
// };
// var per = new Person();
//
// var ff = f1.bind(per, 10, 20);
// ff();
function Person(age) {
this.age=age;
}
Person.prototype.play=function () {
console.log(this+"====>"+this.age);
};
function Student(age) {
this.age=age;
}
var per=new Person(10);
var stu=new Student(20);
//复制了一份
var ff=per.play.bind(stu);
ff();
//bind是用来复制一份
//使用的语法:
/*
* 函数名字.bind(对象,参数1,参数2,...);---->返回值是复制之后的这个函数
* 方法名字.bind(对象,参数1,参数2,...);---->返回值是复制之后的这个方法
*
* */
bind方法的应用
//通过对象,调用方法,产生随机数
function ShowRandom() {
//1-10的随机数
this.number=parseInt(Math.random()*10+1);
}
//添加原型方法
ShowRandom.prototype.show1=function () {
//改变了定时器中的this的指向了,本来应该是window,现在是实例对象了
window.setInterval(this.show2.bind(this),1000);
};
//添加原型方法
ShowRandom.prototype.show2=function () {
//显示随机数--
console.log(this.number);
};
//实例对象
var sr=new ShowRandom();
//调用方法,输出随机数字
//调用这个方法一次,可以不停的产生随机数字
sr.show1();
函数的其他成员
1.name—>函数的名字,name属性是只读的,不能修改
2.arguments—>实参的个数
3.length—>函数定义的时候形参的个数
4.caller—>调用者
//函数中有一个name属性----->函数的名字,name属性是只读的,不能修改
//函数中有一个arguments属性--->实参的个数
//函数中有一个length属性---->函数定义的时候形参的个数
//函数中有一个caller属性---->调用(f1函数在f2函数中调用的,所以,此时调用者就是f2)
function f1(x,y) {
console.log(f1.name);
console.log(f1.arguments.length);
console.log(f1.length);
console.log(f1.caller);//调用者
}
// f1.name="f5";
// f1(10,20,30,40);
// console.dir(f1);
function f2() {
console.log("f2函数的代码");
f1(1,2);
}
f2();
高阶函数
作为参数
function f1(fn) {
console.log("f1的函数");
fn();//此时fn当成是一个函数来使用的
}
//fn是参数,最后作为函数使用了,函数是可以作为参数使用
//传入匿名函数
f1(function () {
console.log("我是匿名函数");
});
//命名函数
function f2() {
console.log("f2的函数");
}
f1(f2);
//函数作为参数的时候,如果是命名函数,那么只传入命名函数的名字,没有括号
function f1(fn) {
setInterval(function () {
console.log("定时器开始");
fn();
console.log("定时器结束");
},1000);
}
f1(function () {
console.log("好困啊,好累啊,就是想睡觉");
});
作为返回值
// function f1() {
// console.log("f1函数开始");
// return function () {
// console.log("我是函数,但是此时是作为返回值使用的");
// }
//
// }
//
// var ff=f1();
// ff();
// var num=10;
// console.log(typeof num);//获取num这个变量的数据类型
// var obj={};//对象
// //判断这个对象是不是某个类型的
// console.log(obj instanceof Object);
// //获取某个对象的数据类型的样子
// //Object.prototype.toString.call(对象);//此时得到的就是这个对象的类型的样子
//
//
//
// //此时输出的是Object的数据类型 [object Object]
// console.log(Object.prototype.toString());
// //输出的数组的数据类型 [object Array]
// console.log(Object.prototype.toString.call([]));
//
// var arr=[10,20,30];
// console.log(Object.prototype.toString.call(arr));
//
console.log(Object.prototype.toString.call(new Date()));
//获取某个对象的类型是不是你传入的类型
//[10,20,30] 是不是"[object Array]"
//type---是变量----是参数----"[object Array]"
//obj---是变量-----是参数----[10,20,30];
//判断这个对象和传入的类型是不是同一个类型
function getFunc(type) {
return function (obj) {
return Object.prototype.toString.call(obj) === type;
}
}
var ff = getFunc("[object Array]");
var result = ff([10, 20, 30]);
console.log(result);
var ff1 = getFunc("[object Object]");
var dt = new Date();
var result1 = ff1(dt);
console.log(result1);
作为参数的练习
// var arr = [1, 100, 20, 200, 40, 50, 120, 10];
// //排序
// arr.sort();
// console.log(arr);
var arr = [1, 100, 20, 200, 40, 50, 120, 10];
//排序---函数作为参数使用,匿名函数作为sort方法的参数使用,那么此时的匿名函数中有两个参数,
arr.sort(function (obj1,obj2) {
if(obj1>obj2){
return -1;
}else if(obj1==obj2){
return 0;
}else{
return 1;
}
});
console.log(arr);
var arr1=["acdef","abcd","bcedf","bced"];
arr1.sort(function (a,b) {
if(a>b){
return 1;
}else if(a==b){
return 0;
}else{
return -1;
}
});
console.log(arr1);
作为返回值的练习
//排序,每个文件都有名字,大小,时间,都可以按照某个属性的值进行排序
//三部电影,电影有名字,大小,上映时间
function File(name, size, time) {
this.name = name;//电影名字
this.size = size;//电影大小
this.time = time;//电影的上映时间
}
var f1 = new File("jack.avi", "400M", "1997-12-12");
var f2 = new File("tom.avi", "200M", "2017-12-12");
var f3 = new File("xiaosu.avi", "800M", "2010-12-12");
var arr = [f1, f2, f3];
function fn(attr) {
//函数作为返回值
return function getSort(obj1, obj2) {
if (obj1[attr] > obj2[attr]) {
return 1;
} else if (obj1[attr] == obj2[attr]) {
return 0;
} else {
return -1;
}
}
}
var ff = fn("name");
//函数作为参数
arr.sort(ff);
for (var i = 0; i < arr.length; i++) {
console.log(arr[i].name + "====>" + arr[i].size + "===>" + arr[i].time);
}
函数闭包
作用域、作用域链、预解析
变量---->局部变量和全局变量,
作用域:就是变量的使用范围
局部作用域和全局作用域
js中没有块级作用域—一对括号中定义的变量,这个变量可以在大括号外面使用
函数中定义的变量是局部变量
while(true){
var num=10;
break;
}
console.log(num);
{
var num2=100;
}
console.log(num2);
if(true){
var num3=1000;
}
console.log(num3);
function f1() {
//局部变量
var num=10;
}
console.log(num);
作用域链:变量的使用,从里向外,层层的搜索,搜索到了就可以直接使用了
层层搜索,搜索到0级作用域的时候,如果还是没有找到这个变量,结果就是报错
var num=10; //作用域链 级别:0
var num2=20;
var str = "abc"
function f1() {
var num2=20;
function f2() {
var num3=30;
console.log(num);
}
f2();
}
f1();
预解析:就是在浏览器解析代码之前,把变量的声明和函数的声明提前(提升)到该作用域的最上面
//变量的提升
console.log(num);
var num=100;
//函数的声明被提前了
f1();
function f1() {
console.log("这个函数,执行了");
}
var f2;
f2=function () {
console.log("小杨好帅哦");
};
f2();
什么是闭包
闭包就是能够读取其他函数内部变量的函数,
由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,
因此可以把闭包简单理解成 “定义在一个函数内部的函数”。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
闭包的用途:
- 可以在函数外部读取函数内部成员
- 让函数内成员始终存活在内存中
//闭包
/*
* 闭包的概念:函数A中,有一个函数B,函数B中可以访问函数A中定义的变量或者是数据,此时形成了闭包(这句话暂时不严谨)
* 闭包的模式:函数模式的闭包,对象模式的闭包
* 闭包的作用:缓存数据,延长作用域链
* 闭包的优点和缺点:缓存数据
*
* 闭包的应用
*
*
* */
//函数模式的闭包:在一个函数中有一个函数
function f1() {
var num=10;
//函数的声明
function f2() {
console.log(num);
}
//函数调用
f2();
}
f1();
//对象模式的闭包:函数中有一个对象
function f3() {
var num=10;
var obj={
age:num
};
console.log(obj.age);//10
}
f3();
function f1() {
var num=10;
return function () {
console.log(num);
return num;
}
}
var ff= f1();
var result= ff();
console.log(result);
function f2() {
var num=100;
return {
age:num
}
}
var obj= f2();
console.log(obj.age);
闭包案例
小案例
//普通的函数
function f1() {
var num = 10;
num++;
return num;
}
console.log(f1());//11
console.log(f1());//11
console.log(f1());//11
//函数模式的闭包
function f2() {
var num = 10;
return function () {
num++;
return num;
}
}
var ff = f2();
console.log(ff());//11
console.log(ff());//12
console.log(ff());//13
案例产生多个相同的随机数
function showRandom() {
var num=parseInt(Math.random()*10+1);
console.log(num);
}
showRandom();
showRandom();
showRandom();
//闭包的方式,产生三个随机数,但是都是相同的
function f1() {
var num=parseInt(Math.random()*10+1);
return function () {
console.log(num);
}
}
var ff=f1();
ff();
ff();
ff();
//总结:如果想要缓存数据,就把这个数据放在外层的函数和里层的函数的中间位置
//闭包的作用:缓存数据.优点也是缺陷,没有及时的释放
//局部变量是在函数中,函数使用结束后,局部变量就会被自动的释放
//闭包后,里面的局部变量的使用作用域链就会被延长
案例点赞应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>对自己狠点</title>
<style>
ul {
list-style-type: none;
}
li {
float: left;
margin-left: 10px;
}
img {
width: 200px;
height: 180px;
}
input {
margin-left: 30%;
}
</style>
<script>
//$永远都是24k纯帅的十八岁的杨哥$
</script>
</head>
<body>
<ul>
<li><img src="images/ly.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
<li><img src="images/lyml.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
<li><img src="images/fj.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
<li><img src="images/bd.jpg" alt=""><br/><input type="button" value="赞(1)"></li>
</ul>
<script>
//获取所有的按钮
//根据标签名字获取元素
function my$(tagName) {
return document.getElementsByTagName(tagName);
}
//闭包缓存数据
function getValue() {
var value=2;
return function () {
//每一次点击的时候,都应该改变当前点击按钮的value值
this.value="赞("+(value++)+")";
}
}
//获取所有的按钮
var btnObjs=my$("input");
//循环遍历每个按钮,注册点击事件
for(var i=0;i<btnObjs.length;i++){
//注册事件
btnObjs[i].onclick=getValue();
}
// var btnObjs=my$("input");
// var value=1;
// //循环遍历每个按钮
// for(var i=0;i<btnObjs.length;i++){
//
// //为每个按钮注册点击事件
// btnObjs[i].onclick=function () {
// console.log("哈哈");
// this.value="赞("+(value++)+")";
// };
// }
</script>
</body>
</html>
沙箱
环境,黑盒,在一个虚拟的环境中模拟真实世界,做实验,实验结果和真实世界的结果是一样,但是不会影响真实世界
var num=100;
(function () {
var num=10;
console.log(num);//10
}());
console.log(num);//100
沙箱案例
<div>这是div</div>
<div>这是div</div>
<div>这是div</div>
<p>这是p</p>
<p>这是p</p>
<p>这是p</p>
<script>
var getTag = 10;
var dvObjs = 20;
var pObjs = 30;
//放进沙箱里 变量的命名就不会冲突了
(function () {
//根据标签名字获取元素
function getTag(tagName) {
return document.getElementsByTagName(tagName)
}
//获取所有的div
var dvObjs = getTag("div");
for (var i = 0; i < dvObjs.length; i++) {
dvObjs[i].style.border = "2px solid pink";
}
//获取所有的p
var pObjs = getTag("p");
for (var i = 0; i < pObjs.length; i++) {
pObjs[i].style.border = "2px solid pink";
}
}());
console.log(getTag);
console.log(dvObjs);
console.log(pObjs);
递归
函数中调用函数自己,此时就是递归,递归一定要有结束的条件
var i = 0;
function f1() {
i++;
if (i < 5) {
f1();
}
console.log("从前有个山,山里有个庙,庙里有个和尚给小和尚讲故事:");
}
f1();
递归案例
//求n个数字的和,5 计算1+2+3+4+5
// var sum=0;
// for(var i=1;i<=5;i++){
// sum+=i;
// }
// console.log(sum);
//递归实现:求n个数字的和 n=5---> 5+4+3+2+1
//函数的声明
function getSum(x) {
if(x==1){
return 1;
}
return x+getSum(x-1);
}
//函数的调用
console.log(getSum(5));
/*
*
* 执行过程:
* 代码执行getSum(5)--->进入函数,此时的x是5,执行的是5+getSum(4),此时代码等待
* 此时5+getSum(4),代码先不进行计算,先执行getSum(4),进入函数,执行的是4+getSum(3),等待, 先执行的是getSum(3),进入函数,执行3+getSum(2),等待,先执行getSum(2),进入函数,执行 2+getSum(1);等待, 先执行getSum(1),执行的是x==1的判断,return 1,所以,
* 此时getSum(1)的结果是1,开始向外走出去
* 2+getSum(1) 此时的结果是:2+1
* 执行:
* getSum(2)---->2+1
* 3+getSum(2) 此时的结果是3+2+1
* 4+getSum(3) 此时的结果是4+3+2+1
* 5+getSum(4) 此时的结果是5+4+3+2+1
*
* 结果:15
*
*
*
* */
//递归案例:求一个数字各个位数上的数字的和: 123 --->6 ---1+2+3
//523
function getEverySum(x) {
if(x<10){
return x;
}
//获取的是这个数字的个位数
return x%10+getEverySum(parseInt(x/10));
}
console.log(getEverySum(1364));//5
//递归案例:求斐波那契数列
function getFib(x) {
if(x==1||x==2){
return 1
}
return getFib(x-1)+getFib(x-2);
}
console.log(getFib(12));