同步异步简单了解(闭包的使用)
同步:代码的书写顺序和代码的执行顺序一样。 (大部分为同步代码)
异步:代码的书写顺序和代码的执行顺序不一样。
在JS中,异步代码仅仅是个别:
1)事件绑定
2)定时器
3)ajax
- 每一个元素对象默认里面就有很多的属性 其中有一个叫onclick.
给元素注册点击事件 btn叫事件源
click叫事件类型
funciotn 事件处理程序 事件发生时做什么
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
let btns = document.getElementsByTagName("button"); // 数组
//document.getElementsByTagName("button"); 存储在堆中
btns[0].onclick = function () {
console.log(0)
}
//函数肯定要调用 当我们点击了第1上按钮,浏览器会帮我们调用后面的函数
btns[1].onclick = function () {
console.log(1)
}
btns[2].onclick = function () {
console.log(2)
}
</script>
1、
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
var btns = document.getElementsByTagName("button"); //数组容器,也是个对象
for (var i=0; i<btns.length; i++){
btns[i].onclick = (function (i) { //事件绑定,浏览器在执行时直接忽略掉
return function g() {
console.log(i)
}
})(i);
}
</script>
结果展示:分别点三个按钮,会出现0,1,2;
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
var btns = document.getElementsByTagName("button");
for (var i=0; i<btns.length; i++){
// console.log(btns[i].abc)// 访问一个对象上不存在的属性 得到的是und
btns[i].index = i; // 把i值赋值给对象上的index属性
btns[i].onclick = function () {
// console.log(i)
// console.log(btns[i]) // 答:浏览器帮我们调用函数 浏览器帮我们调用此函数时,是得不到全局EC中的数据,
console.log(this.index) // 解决办法:this this表示当前的事件源
// console.log(btns[i].index)
}
}
</script>
结果展示:分别点三个按钮,会出现0,1,2;
3.
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<script>
var btns = document.getElementsByTagName("button");
for (let i=0; i<btns.length; i++){
btns[i].onclick = function () {
console.log(i)
}
}
</script>
结果展示:分别点三个按钮,会出现0,1,2;
this相关
1)如果this出现在普通的函数中,this表示window.
- 如果你通过window打点调用一个函数,这个函数中的this也是window。
程序员自己调用一个函数 一个普通函数 函数中的this表示window
<button id="box">box</button>
<script>
let box = document.getElementById("box");
function f() {
console.log(this); // window
}
f();
box.onclick = f();
</script>
结果展示:f()表示自己调用个函数
<!-- <script>
function k() {
console.log(this); // window
}
window.k(); // window打点调用
k(); // 程序员自己调用
</script> -->
2)事件绑定,事件处理程序,事件发生时,浏览器帮我们调用这个此函数中的this表示事件源
<button id="box">box</button>
<script>
let box = document.getElementById("box");
box.onclick = function () {
console.log(this); //事件源
}
</script>
结果展示:点击盒子按钮,显示事件源
3)在一个对象中,如果有方法(函数),如果通过这个对象调用方法中的this表示这个对象。一个方法中的this是谁,就看点前面是谁,是谁在调用。
<script>
var wc = {
name:"wangcai", // 属性
age:100,
eat:function () { // 叫方法(属性)
console.log("eat...")
console.log(this);
}
}
wc.eat(); // 调用一个对象上的方法
// 一个方法中的this是谁,就看点前面是谁。
</script>
<script>
var fullname = "language";
var obj = {
fullname:"javascript",
prop:{
getFullName:function () {
return this.fullname;
}
}
}
console.log(obj.prop.getFullName()); // undefined
</script>
解释: 谁用了getFullName 看.前面是谁。点前面是obj.prop 说明getFullName中的this是obj.prop。obj.prop中没有fullname;访问一个对象上不存在的属性,得到undeifined
4)在IIFE中,this表示window
<script>
(function () {
console.log(this);
})(); // 在非严格模式下,IIFE中的this表示window
</script>
5)前四点都是在非严格模式下。在严格模式下,调用一个普通函数,t表示undefined。IIFE中的this也表示undefined
<script>
"use strict"; // 启用JS的严格模式
function f() {
console.log(this); // undefined
}
f();
(function () {
console.log(this); // undefined
})();
</script>
练习题:
1.
<script>
var num = 10; // VO ==》 num也变成了65
var obj = {
num:20, // num:30
}
obj.fn = (function (num) {
this.num = num*3; //
num++; //
console.log(num)
return function (n) {
// EC n:5
// EC n:10
this.num += n;
num++;
console.log(num)
}
})(obj.num)
var fn = obj.fn;
fn(5);
obj.fn(10);
console.log(num,obj.num)
console.log(window.num)
</script>
结果展示:
<script>
(function () {
var a = 1;
var obj = {
a:10,
f:function () {
a *= 2;
}
}
obj.f();
alert(obj.a+a)
})();
</script>
结果展示:
解释:如果要调用obj中的a:10,必须是obj.a
call/apply/bind相关
1、call
相关:
作用: 1)call改变this指向,让函数中的this指向call中参数(对象)
2)让函数直接执行 调用
3)call可以让一个数据去借用一个对象上的方法
1)举例说明:为什么要修改this的指向
以往写法:
<script>
var obj1 = { name: "wnagcai", say: function () { console.log("我是:" +this.name) } }
var obj2 = { name: "xiaoming", say: function () { console.log("我是:" +this.name) } }
obj1.say();
obj2.say();
</script>
- 因为如果原先写的话同样的函数写了两遍,占了两个堆内存
改进一:
<script>
var abc = function () {
console.log("我是" + this.name)
}
var obj1 = {
name: "wangcai",
say: abc
}
var obj2 = {
name: "xiangming",
say: abc
}
console.log(obj1.say === obj2.say)
obj1.say();
obj2.say();
</script>
改进二:
<script>
var abc = function () {
console.log("我是" + this.name)
}
var obj1 = {
name: "wangcai",
say: abc
}
var obj2={
name:"xioaming",
say:abc
}
abc.call(obj1);
abc.call(obj2);
</script>
结果展示:
解释:call的参数,让this指向obj1,改变this的指向,并且让abc进行直接执行调用
2)call可以让一个数据去借用一个对象上的方法
小案例:如何准确的得出数据类型
- 利用
typeof
console.log(typeof 1) // number
console.log(typeof "hello") // string
console.log(typeof true) // boolean
function f(){}
console.log(typeof f) // function
var a;
console.log(typeof a) // undefined
console.log(typeof [1,2,3]) // object
console.log(typeof {a:1}) // object
typof
的不足:检测基本数据类型OK,对于引用数据类型来说,检测function也OK,但是其它的不OK。
- 用
tostring
可以显示数据类型,除了Object里有toString之外,其他的也有toString方法;如果自己有toString方法,在调用的时候,就调用原型的toString方法。
<script>
let arr = [1, 2, 3];
console.log(arr.toString()); //调用的是Array.prototype.toString()
let obj = { name: "wc" }
console.log(obj.toString()); //调用的是Object.prototype.toString()
let f = function () { }
console.log(f.toString()); //调用的是Function.prototype.toString()
</script>
<script>
var arr1 = [1, 2, 3];
arr1.toString = function () {
console.log("xxx")
}
console.log(arr1.toString()); //调用的是自己私有的toString方法
</script>
- 直接去调用Object上面的
toString
方法
var arr2 = [1111];
Object.prototype.toString.call(arr2); //arr2直接调用Object的原型对象上面的toString
- 利用
Object.prototype.toString.call
可以非常精确地检测一个数据的数据类型
<script>
console.log(Object.prototype.toString.call(1)); //[object Number]
console.log(Object.prototype.toString.call("hello"));//[object String]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call({})); //[object Object]
console.log(Object.prototype.toString.call([1, 2, 3])); //[object Array]
function f() { }
console.log(Object.prototype.toString.call(f)); //[object Function]
let d =new Date();
console.log(Object.prototype.toString.call(d)); //[object Date]
let r=new RegExp;
console.log(Object.prototype.toString.call(r)); //[object RegExp]
</script>
- 通过上面的
Object.prototype.toString.call
,我们确实准确的得到了他的数据类型的,但是我们想要单独显示数据类型,这就用到了.substring,进行切割选择输出的内容。
str.substring(indexStart[, indexEnd])
indexStart:需要截取的第一个字符的索引
indexEnd:可选。一个 0 到字符串长度之间的整数,以该数字为索引的字符不包含在截取的字符串内。
<script>
function getType(abc) {
var rs = Object.prototype.toString.call(abc); //得到[object ...]
rs = rs.substr(8) // rs.substr(8)得到除去[object
var len = rs.length;
//console.log(rs.substr(0,len-1)) //rs.substr(0,len-1) 减去后面的],得到一个首字母大写的数字类型
return rs.substr(0, len - 1).toLowerCase() //toLowerCase()变成小写
}
console.log(getType(123));
console.log(getType([]));
console.log(getType({}));
console.log(getType(new Date));
console.log(getType(new RegExp));
var f = function () { }
console.log(getType(f));
</script>
结果:
解释:通过上面的一个简单函数,去进行查看一些数据的数据类型
3)call的常见用法
1.如何把字符串中的数字进行翻转
<script>
var str = "123456789";
var newstr = "";
for (var i = str.length - 1; i >= 0; i--) {
newstr += str[i];
}
console.log(newstr); //987654321
</script>
- 了解数组中有一个可以翻转的方法:
reverse()
<script>
var arr = [1, 2, 3, 4, 5]
console.log(arr.reverse());//[5,4,3,2,1]
</script>
- 利用数组的方法让字符串的翻转
<script>
//一步一步的,先转化,后翻转,再转化
var str = "123456789";
console.log(str.split("")); //["1", "2", "3", "4", "5", "6", "7", "8", "9"]
console.log(str.split("").reverse()); //["9", "8", "7", "6", "5", "4", "3", "2", "1"]
console.log(str.split("").reverse().join()); //9,8,7,6,5,4,3,2,1
console.log(str.split("").reverse().join("")); //987654321
</script>
结果显示:
- 利用call进行整合
<script>
var str = "123456789";
console.log(Array.prototype.reverse.call(str.split("")).join("")); // 987654321
</script>
2.如何把伪数组转化成一个真正的对象
伪数组:不是真正的数组;本质是对象,没有数组原型对象上的方法
真正的数组:[1,2,3][“a”,“b”,“c”]
伪数组:{0:“a”,1:“b”,2:“c”}
常见的伪数组:
1)通过doucment.getElementsByTagName获得的元素集合就是伪数组
2)函数内部的arguments
代码解释伪数组:
<span>1</span><span>2</span><span>3</span>
<script>
var spans = document.getElementsByTagName("span");
console.dir(spans)
//此时spans不是数组,仅仅长的像数组罢了
</script>
<script>
// arguemnts是函数内部的一个对象 用来收集实参
function f(a, b) {
console.log(a);
console.log(b);
// 函数调用时,不只把实参给了形参,也把实参给了augments,arguments会收集实参
console.dir(arguments); // 伪数组
}
f(1, 2);
//console.log(arguments); // 报错ReferenceError,函数外部是不能访问arguments的
</script>
- 把一个伪数组(对象)转化成真正的数组
<span>1</span><span>2</span><span>3</span>
<script>
var spans = document.getElementsByTagNam("span");
var arr = [];
for (let i = 0; i < spans.length; i++) {
arr.push(spans[i]);
}
console.log(arr);
</script>
- 借用
slice
方法:方法返回一个新的数组对象,
<span>1</span><span>2</span><span>3</span>
<script>
var spans=document.getElementsByTagName("span");
//console.log(Array.isArray(spans)); //false
//借用slice方法:返回一个新的数组对象,
console.log(Array.prototype.slice.call(spans));
console.log(Array.isArray(Array.prototype.slice.call(spans))); //true
</script>
结果展示:
3.call方法里面可以有多个参数
<script>
function f(a,b){
return a+b;
}
var obj={};
//f.call(obj); //让f执行,
//f(1,2); //自己调用f参数,并进行传递参数
console.log(f.call(obj,4,5));
</script>
解释:call方法里面可以有多个参数,第一个参数表示谁去借用这个方法 ,后面的参数都是给这个方法传递的参数
2、apply
相关:
和call的用法一样,唯一就是传递参数有区别
代码展示:
<script>
function f(a, b) {
// console.log("f...")
return a + b;
}
var obj={};
console.log(f.apply(obj,[1,2])); //让obj借的函数,传参传的是个数组
</script>
小案例:
** 获取数组中最大值 ,利用Math类**
<script>
console.log(Math.max(2,4,1,6));
var arr = [2, 3, 4, 5, 6, 9, 10, 29, 3, 0];
console.log(Math.max.call(arr,1,2,3,4,6,5,8));
console.log(Math.max.apply(arr,[1,2,3,4,6,5,8]));
console.log(Math.max.apply(arr,arr));
console.log(Math.max.apply({},arr));
console.log(Math.max.apply("", arr));
console.log(Math.max.apply(null, arr));
console.log(Math.max.apply("abc", arr));
</script>
3、bind
相关:
bind:绑定的意思,改变this的方向,但是函数不执行
代码展示:
<script>
function f(a, b) {
//console.log("f...")
return a + b;
}
var obj = {};
let k = f.bind(obj);
console.log(k(1, 2));
</script>
总结:
call
改变了this指向 并让函数执行,传递参数一个个传递
apply
改变了this指向 并让函数执行,传送参数必须是数组
bind
改变了this指向 函数不执行