ES 5 复习
复习进度
- JavaScript组成部分
- JavaScript 数据类型
- JavaScript 数组
- JavaScript字符串
JavaScript的组成部分
ECMAScript :规范,定义了JS语言一系列语言规范
DOM:HTML文档结构,DOM操作
BOM:浏览器对象,提供与浏览器相关的一系列API,便于我们操作与浏览器相关的功能
BOM的组成:window、Location、Screen、History,Navigator、Document
-
History:window下的对象,封装了多浏览器历史记录操作的方法,前一页 后一页 (React、Vue 路由的底层实现)
-
Location:用来操作和获取地址栏相关信息
-
Screen:获取屏幕宽度、高度等相关信息
-
Navigator:获取用户浏览器相关信息
JavaScript 数据类型
数值
字符串
对象
boolean
null:特殊的对象
undefined:undefined
######function
JavaScript的typeof返回哪些数据类型
typeof函数返回值类型
参数:将要被判断的变量
返回值:字符串
支持返回的数据类型:number 、 boolean、object、undefined、string、function
//typeof 函数
//返回值:类型:字符串 描述当前变量的数据类型
var number = 10;
window.console.log(typeof(typeof(number)))
typeof可以检测引用数据类型的,但是他不能检测到该引用类型的对象到底是哪个引用类型,他会统一返回object
基本数据类型和引用数据类型及区别
######深拷贝
JSON内置对象深拷贝
JSON 对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝
var a = {age:1,name:'ccy',info:{address:'wuhan',interest:'playCards'}};
var b = JSON.parse(JSON.stringify(a));
a.info.address = 'shenzhen';
console.log(a.info,b.info);
3种强制类型转换和2种隐式类型转换
将一种数据类型转换为另一种数据类型
强制类型转换:
parseInt:将其他数据类型转换为整数
parseFloat:将其他数据类型转换为浮点数
Number:将其他数据类型转换为数值
隐式类型转换:
在计算的时候,隐式将参与计算的不同数据类型的变量,先转换为相同数据类型的变量,然后再计算
==之间发生的隐式类型转换;
=== 不仅要验证值是否相等,同时还要验证数据类型是否一致
+、*、/、-操作符的隐式类型转换;
辑运算符 || &&;
null和undefined的区别
null是个对象,引用类型 堆。 =====》 开辟空间 然后占着
undefined:数据类型 ==== 开辟一个空间,放进去一个undefined
http://www.ruanyifeng.com/blog/2014/03/undefined-vs-null.html
数组
数组的方法及属性
数组的属性
length
Length属性可设置或返回数组中元素的数目。
var arr = new Array(3)
arr[0] = "John"
arr[1] = "Andy"
arr[2] = "Wendy"
document.write("Original length: " + arr.length)
document.write("<br />")
arr.length=5
document.write("New length: " + arr.length)
prototype
prototype 属性使您有能力向对象添加属性和方法。
object.prototype.name=value
数组的方法
concat
concat方法通过合并(连接)现有数组来创建一个新数组:
var myGirls = ["Cecilie", "Lone"];
var myBoys = ["Emil", "Tobias", "Linus"];
var myChildren = myGirls.concat(myBoys); // 连接 myGirls 和 myBoys
concat() 方法不会更改现有数组。它总是返回一个新数组。
concat() 方法可以使用任意数量的数组参数
slice
slice() 方法用数组的某个片段切出新数组。slice() 方法创建新数组。它不会从源数组中删除任何元素;
var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(1);
slice() 可接受两个参数,比如 (1, 3);该方法会从开始参数选取元素,直到结束参数(不包括)为止。
join
join() 方法也可将所有数组元素结合为一个字符串。
var fruits = ["Banana", "Orange","Apple", "Mango"];
document.getElementById("demo").innerHTML = fruits.join(" * ");
split
split() 方法用于把一个字符串分割成字符串数组。
一个字符串数组。该数组是通过在 separator 指定的边界处将字符串 stringObject 分割成子串创建的。返回的数组中的字串不包括 separator 自身。
但是,如果 separator 是包含子表达式的正则表达式,那么返回的数组中包括与这些子表达式匹配的字串(但不包括与整个正则表达式匹配的文本)。
String.split() 执行的操作与 Array.join 执行的操作是相反的。
splice
splice() 方法可用于向数组添加新项
第一个参数(2)定义了应添加新元素的位置(拼接)。
第二个参数(0)定义应删除多少元素。
其余参数(“Lemon”,“Kiwi”)定义要添加的新元素。
splice() 方法返回一个包含已删除项的数组:
var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.splice(2, 2, "Lemon", "Kiwi");
清空数组的方法
length
用length方法可以很轻松地清空数组,代码示例:
var arr = [1,2,3];
console.log(arr);
arr.length = 0;
console.log(arr);
splice
splice() 方法向/从数组中添加/删除项目,然后返回被删除的项目
var arr = [1,2,3];
console.log(arr);
arr.splice(0);
console.log(arr);
[]
var arr = [1 ,2 ,3];
console.log(arr);
arr = []; console.log(arr);
伪数组
具有length属性,其他属性(索引)为非负整数(对象中的索引会被当做字符串来处理,这里你可以当做是个非负整数串来理解),不具有数组的方法。
var fakeArray = {
"0":"aaa",
"1":23,
length:2
};
for (var i=0;i<fakeArray.length;i++){
console.log(fakeArray[i])
}
常见的伪数组(类数组对象)
{“0”:"a,“1”:b}
- 函数内部的
arguments
- DOM对象列表(
document.getElementsByTags
) - jQuery对象(
$("div")
)
转为数组的方法
- 定义伪数组
let arr = [].slice.call(pagis)
pagis.slice()
console.log(arr) 这时arr就是真数组了。
- Array.prototype
let arr = Array.prototype.slice.call(pagis);
利用了slice传一个数组/集合,就会直接返回这个集合的原理。拿到的也是数组。
也就可以使用数组的各种方法了。
- for循环
1 var arr1 = [],
2 len1 = pagis.length;
3 for (var i = 0; i < len1; i++) {
4 arr1.push(pagis[i]);
5 }
就是简单的for循环,把类数组的每一项都push到真正的数字arr1中
与之类似的另一种写法:
(转换函数中的arguments伪数组为真数组,是在学习es6时,将扩展运算符的收集功能在经过babel转换后得到的es5代码)
1 for (var _len = arguments.length, arr = new Array(_len), _key = 0; _key < _len; _key++) {
2 arr[_key] = arguments[_key];
3 }
- bind
1 var func = Function.prototype.call.bind(Array.prototype.slice);
1 var func = Function.prototype.bind(Array.prototype.slice).call;
var func = Function.prototype.call()
func(类数组对象) ===》 Function.prototype.call(类数组对象) ===》 类数组对象.slice()
2 console.log('类数组转换成数组:', func(pagis));
- …解构赋值
1 function args(){
2 console.log(arguments);
3 let newArr = [...arguments];
4 console.log(newArr);
5 }
6 args(1,2,3,23,2,42,34);
void add(){
arguments
}
add(a:1,b:2,c:3,d:4,c:5)
手写冒泡排序和选择排序
冒泡:
[3,44,38,5,47,25,36,2,79,8,1]
var arr = [3,38,44,99];
//定义循环次数,之前比较出的数值,不参与下一次的比较
for(var j = 0; j <= (arr.lenght-1)-1 ;j++){
//上一次比较出的数值,不参与下一次循环
for(var i = 0;i<= (arr.lenght-1) - 1;i++){
//内层循环,每次循环比较一个最大值,然后交换位置
if (arr[i] > arr[i + 1]){
var middle = 0;
middle = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = middle;
}
}
}
consle.log(arr);
var a =10;
var b = 20;
var temp;
temp = a;//10
a=b;//b
b=temp//a
选择:
var arr = [2,6,9,99,8,38,67,22];
//外层循环,实现排序循环次数,次数是单元个数 -1
for (var j = 0;j <= (arr.length-1) -1; j++){
//先默认起始位置就是最小值位置,存储起始位置的索引,也就是 j,之前的起始位置,不参与下一次循环
var min = j;//0
// 默认的最小值位置是当前循环的起始位置,是j,比较要从下一个位置开始,内层循环起始,是比较的起始位置+1开始循环
for(var i = j+1; i <= arr.length-1 ; i++){
//如果有单元的数值,小于存储的索引对应的数值
if(arr[min] > arr[i]){
min = i;//下标5
}
}
//内层循环执行完毕,存储的索引下标如果不是起始的索引j,就交换 min中存储的索引下标对应的数值 和 j索引下标应的数值
if(min != j){
var m = 0;
m = arr[j];
arr[j] = arr[min];
arr[min] = m;
}
}
console.log(arr);
JavaScript的事件流模型都有什么
冒泡事件流:
当触发一个节点的事件时,会从当前节点开始,依次触发其祖先节点的同类型事件,直到DOM根节点 。
捕获事件流:
当触发一个节点的事件时,会从DOM根节点开始,依次触发其祖先节点的同类型事件,
直到当前节点自身 。
DOM事件流:
dom同时支持两种事件模型,但捕获性事件先开始,从document开始也结束于document,dom模型的独特之处在于文本也可以触发事件
阻止默认事件
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
preventDefault它是事件对象(Event)的一个方法,作用是取消一个目标元素的默认行为。既然是说默认行为,当然是元素必须有默认行为才能被取消,如果元素本身就没有默认行为,调用当然就无效了。什么元素有默认行为呢?如链接,提交按钮等。当Event 对象的 cancelable为false时,表示没有默认行为,这时即使有默认行为,调用preventDefault也是不会起作用的。
我们都知道,链接的默认动作就是跳转到指定页面,下面就以它为例,阻止它的跳转:
a.onclick =function(e){
if(e.preventDefault){
e.preventDefault();
}else{
window.event.returnValue == false;
}
}
return false
javascript的return false只会阻止默认行为,而是用jQuery的话则既阻止默认行为又防止对象冒泡。
下面这个使用原生js,只会阻止默认行为,不会停止冒泡
var a = document.getElementById("testB");
a.onclick = function(){
return false;
};
阻止事件冒泡
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
stopPropagation也是事件对象(Event)的一个方法,作用是阻止目标元素的冒泡事件,但是会不阻止默认行为。什么是冒泡事件?如在一个按钮是绑定一个”click”事件,那么”click”事件会依次在它的父级元素中被触发 。stopPropagation就是阻止目标元素的事件冒泡到父级元素。
阻止冒泡
//code from http://caibaojian.com/javascript-stoppropagation-preventdefault.html
window.event? window.event.cancelBubble = true : e.stopPropagation();
function stopBubble(e) {
//如果提供了事件对象,则这是一个非IE浏览器
if ( e && e.stopPropagation )
//因此它支持W3C的stopPropagation()方法
e.stopPropagation();
else
//否则,我们需要使用IE的方式来取消事件冒泡
window.event.cancelBubble = true;
}
stopBubble();
事件委托
事件委托就是利用冒泡的原理,将事件加到 父元素 或 祖先元素上,触发执行效果。
var ul = document.querySelector("ul");
ul.onclick = function(e){
var e = e || window.event;
var target = e.target || e.srcElement;
if(target.nodeName.toLowerCase() === "li"){
alert("li");
}
}
var ul = document.querySelector("ul");
ul.onclick = function(e){
//在不同浏览器下,获取事件发生的目标元素
var e = e || window.event,
target = e.target || e.srcElement;
if(target === li[i]){
var li=this.querySelectorAll("li");
var index = li.indexof(target)
index = Array.prototype.indexOf.call(li,target);
alert("所点击 li 的下标是:" + index);
}
}
cookies
https://www.runoob.com/js/js-cookies.html
DOM操作
https://www.cnblogs.com/wfblog/p/8862946.html
字符串
方法及属性
length
内置属性 length 来计算字符串的长度:
var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var sln = txt.length;
方法 | 描述 |
---|---|
charAt() | 返回指定索引位置的字符 |
charCodeAt() | 返回指定索引位置字符的 Unicode 值 |
concat() | 连接两个或多个字符串,返回连接后的字符串 |
fromCharCode() | 将 Unicode 转换为字符串 |
indexOf() | 返回字符串中检索指定字符第一次出现的位置 |
lastIndexOf() | 返回字符串中检索指定字符最后一次出现的位置 |
localeCompare() | 用本地特定的顺序来比较两个字符串 |
match() | 找到一个或多个正则表达式的匹配 |
replace() | 替换与正则表达式匹配的子串 |
search() | 检索与正则表达式相匹配的值 |
slice() | 提取字符串的片断,并在新的字符串中返回被提取的部分 |
split() | 把字符串分割为子字符串数组 |
substr() | 从起始索引号提取字符串中指定数目的字符 |
substring() | 提取字符串中两个指定的索引号之间的字符 |
toLocaleLowerCase() | 根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射 |
toLocaleUpperCase() | 根据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射 |
toLowerCase() | 把字符串转换为小写 |
toString() | 返回字符串对象值 |
toUpperCase() | 把字符串转换为大写 |
trim() | 移除字符串首尾空白 |
valueOf() | 返回某个字符串对象的原始值 |
字符方法charAt()和charCodeAt()
var str='Hello World';//创建字符串
//1:测试charAt()方法
console.log(str.charAt(1));//返回e
//2:测试charCodeAt()方法
console.log(str.charCodeAt(1));//返回101(ASCII编码)
console.log(str[1]);//返回e
字符串操作方法concat()、slice()、substr()、substring()
//1:测试concat()方法
var str1='Hello ';
var result=str1.concat('World');
console.log(str1); //Hello
console.log(result);//Hello World
//2:测试slice(startIndex,[lastIndex])方法
//参数:开始下标,结束下标(可选)
var stringValue='hello world';
console.log(stringValue.slice(3));//lo world
console.log(stringValue.slice(3,7));//lo w
//3:测试substr(startIndex,[lastIndex])方法
//参数:开始下标,结束下标(可选)
console.log(stringValue.substr(3));//lo world
console.log(stringValue.substr(3,7));// lo worl
//4:测试substring(startIndex,[lastIndex])方法
//参数:开始下标,结束下标(可选)
console.log(stringValue.substring(3));//lo world
console.log(stringValue.substring(3,7));//lo w
var item='hello world';
console.log(item.slice(-3));//rld
console.log(item.substr(-3));//rld
console.log(item.substring(-3));//hello world
console.log(item.slice(3,-4));//lo w
console.log(item.substr(3,-4));//''空字符串
console.log(item.substring(3,-4));//hel
这三个方法都返回被操作字符串的一个字符串,而且也接受一个或两个参数,当接受两个参数时,不包含结束下标,第一个参数指定字符串的起始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束,具体来说,slice()和substring()的第二个参数指定的是字符串最后一个字符后面的位置,而substr()的第二个参数指定的则是返回的字符个数。如果没有给这些方法指定第二个参数,则将字符串的末尾作为结束位置。
在传递这些方法的参数是负值的情况下,它们的行为就不尽相同了,其中slice()方法会将传入的负值与字符串长度相加,substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0。最后,substring()方法会将所有负值参数转换为0。
字符串位置方法indexOf()和lastIndexOf()
var stringValue='hello world';
//1:测试inexOf()方法
console.log(stringValue.indexOf('o'));//4
console.log(stringValue.indexOf('o',6));//7
//2:测试lastIndexOf()方法
console.log(stringValue.lastIndexOf('o'));//7
console.log(stringValue.lastIndexOf('o',6));//4
var item='Lorem ipsum dolor sit amet, consectetur adipisicing elit';
var positions=new Array();
var pos=item.indexOf('e');
while(pos>1){
positions.push(pos);
pos=item.indexOf('e',pos+1);
}
console.log(positions);//3,24,32,35,52;
trim()方法
var str=' hello world ';
var trimStr=str.trim();
console.log(str);// hello world
console.log(trimStr);//hello world
字符串大小写转换方法toLowerCase()和toUpperCase()
var str='Hello World';
console.log(str.toLowerCase()); //hello world
console.log(str.toUpperCase());//HELLO WORLD
console.log(str.toLocaleLowerCase());//hello world
console.log(str.toLocaleUpperCase());//HELLO WORLD
字符串的模式匹配方法split()、match()、replace()、search()
//1:测试match()方法
var text1='cat, bat, sat, fat';
var pattern=/.at/;
var matches=text1.match(pattern);
console.log(matches.index);//0
console.log(matches[0]);//cat
console.log(pattern.lastIndex);//0
//2:测试search()方法
var text2='cat bat, sat, fat';
var pos=text2.search(/at/);
console.log(pos);//1
//3:测试replace()方法
var text3='cat, bat, sat, fat';
var result=text3.replace('at','ond');
console.log(result);//cond,bat,sat,fat
result =text3.replace(/at/g,'ond');
console.log(result);//cond,bond,sond,fond
//4:测试split()方法
var text4='red,blue,green,yellow';
var colors1=text4.split(',');
var colors2=text4.split(',',2);
console.log(colors1);//['red','blue','green','yellow'];
console.log(colors2);//['red','blue'];
match()方法本质上与调用RegExp的exec()方法相同,match()方法只接受一个参数,要么是一个正则表达式,要么是一个RegExp对象。
search()方法与match()方法的参数相同,有字符串或RegExp对象指定的一个正则表达式,search()方法返回字符串中第一个匹配项的索引,如果没有找到匹配项,则返回-1,而且,search()方法始终从字符串开头向后匹配查找模式。
replace()方法接收两个参数,第一个参数可以是一个RegExp对象或者一个字符串(这个字符串不会被转换成正则表达式),第二个参数可以可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只会替换第一个子字符串。要想替换所有子字符串,唯一的方法就是提供一个正则表达式,而且要指定全局(g)标志。
split()方法可以基于指定的分隔符将一个字符串分割成多少个字符串,并将结果放在数组中。分隔符可以是字符串,也可以是一个RegExp对象(这个方法不会将字符串看成正则表达式)。split()方法可以接受可选的第二个参数,用于指定数组的大小,以确保返回的数组不会超过既定大小。
解析 URL Params 为对象
“https://www.baidu.com/v2/api?username=wangkai&age=2&sex=nv”
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>将URL的参数解析为一个对象</title>
</head>
<body>
<script type="text/javascript">
function queryURL(url){
var arr1 = url.split("?");//["https://www.baidu.com/v2/api","username=wangkai&age=2&sex=nv"]
var params = arr1[1].split("&");["username=wangkai","age=2","sex=nv"]
var obj = {};//声明对象
for(var i=0;i<params.length;i++){
var param = params[i].split("=");[username,wangkai]
obj[param[0]] = param[1];//为对象赋值
}
return obj;
}
var a = queryURL("http://www.baidu.com?name=javascript&keyword=word");
alert(a.name);
</script>
</body>
</html>
转为驼峰
传传统方法
1.转大写,需要用到字符串的toUpperCase()方法
2.去掉-,需要用到字符串方法split(),这样就转成数组了,但数组中的每一个元素依然是字符串,所以可以用循环的方法取到第一个后面的元素
3.取第一个后面的元素的第一个字符,需要用到字符串的charAt()方法
4.第一个字符后面的字符,可以通过字符串截取方法substring()获得,这时把两个拼接再赋回给原数组。即完成了转换
5.用join()方法把数组的逗号去掉,拼接成字符串
var str="border-bottom-color";
function tf(){
var arr=str.split("-");
for(var i=1;i<arr.length;i++){
arr[i]=arr[i].charAt(0).toUpperCase()+arr[i].substring(1);
}
return arr.join("");
};
tf(str);
正则的方法
分析:
1.正则为-字符,即var re=/-\w/g;匹配到-字符
2.用正则的replace()方法替换这个规范为去掉-以及字符大写,通过回调函数第一个参数直接转大写
var str="border-bottom-color";
function tf(){
var re=/-(\w)/g;
str=str.replace(re,function($0,$1){
return $1.toUpperCase();
});
alert(str)
};
tf(str);
查找字符串中出现最多的字符和个数
var str = "nininihaoa";
var object ={};
for (var i = 0, length = str.length; i < length; i++) {
var char = str.charAt(i);
if (object[char]) {//object[n]
//object[key]
object[char]++; //次数加1
} else {
object[char] = 1; //若第一次出现,次数记为1 {n:2,i:1}
}
}
console.log(o); //输出的是完整的对象,记录着每一个字符及其出现的次数
//遍历对象,找到出现次数最多的字符的次数
{
n:3,
i:3,
h:1,
a:2,
o:1
}
var max = 0;
for (var key in object) {
if (max < object[key]) {//3
max = object[key]; //max始终储存次数最大的那个3
}
}
for (var key in o) {
if (o[key] == max) {
//console.log(key);
console.log("最多的字符是" + key);
console.log("出现的次数是" + max);
}
}
统计次数
function countInstances(mainStr, subStr)
{
var count = 0;
var offset = 0;
do
{
offset = mainStr.indexOf(subStr, offset);
if(offset != -1)
{
count++;
offset += subStr.length;
}
}while(offset != -1)
return count;
}
var str = "zhaochucichuzuiduodezifu";
var o = {};
//遍历str,统计每个字符出现的次数
for (var i = 0, length = str.length; i < length; i++) {
//当前第i个字符
var char = str.charAt(i);
//char就是对象o的一个属性,o[char]是属性值,存储出现的次数
if (o[char]) { //如果char属性存在,属性值+1
o[char]++; //次数加1
} else { //char属性不存在为1(即字符第一次出现)
o[char] = 1; //若第一次出现,次数记为1
}
}
//输出的是完整的对象,记录着每一个字符及其出现的次数
//输出{a:1, c:3, d:2, e:1, f:1, h:3, i:3, o:2, u:5, z:3}
console.log(o);
//遍历对象,找到出现次数最多的字符和次数
var max = 0; //存储出现次数最多的次数
var maxChar = null; //存储出现次数最多的字符
for (var key in o) {
if (max < o[key]) {
max = o[key]; //max始终储存次数最大的那个
maxChar = key; //那么对应的字符就是当前的key
}
}
console.log("最多的字符是" + maxChar);
console.log("出现的次数是" + max);
字符串加千分符
var str = '23,598,445,646,544';
var iNum = 10;
function test3(str) {
var iNum = str.length%3; //余数 2
var prev = '';
var iNow = 0;
var tmp = '';
if(iNum !=0) {
prev = str.substring(0,iNum); //将余数截取出来 2
arr.push(prev);
}
str = str.substring(iNum);
for(var i=0;i<str.length;i++) {
iNow++;
tmp +=str[i];
if(iNow ==3 && tmp) {
arr.push(tmp);
tmp = '';
iNow = 0;
}
}
return arr.join(',');
}
alert(test3(str));
函数
函数声明和函数表达式区别
javascript中声明函数的方法有两种:函数声明式和函数表达式
区别如下:
-
以函数声明的方法定义的函数,函数名是必须的,而函数表达式的函数名是可选的。
-
以函数声明的方法定义的函数,函数可以在函数声明之前调用,而函数表达式的函数只能在声明之后调用。
function boo(){
};
var bar = function(){
};
class 关键字
class Student{
}
var student = new Student();
在ECMAScript中,有两个最常用的创建函数对象的方法,即使用函数表达式或者使用函数声明。对此,ECMAScript规范明确了一点,即是,即函数声明 必须始终带有一个标识符(Identifier),也就是我们所说的函数名,而函数表达式则可以省略。说到这里,答案就不言而喻了(前一个是函数声明,后一个是函数表达式)。
函数声明:
function 函数名称 (参数:可选){ 函数体 }
函数表达式:
var func = function 函数名称(可选)(参数:可选){ 函数体 }
JavaScript中callee和caller的作用
callee
runtime
callee是对象的一个属性,该属性是一个指针,指向参数arguments对象的函数
作用:就是用来指向当前对象
返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文.
callee是arguments 的一个属性成员,它表示对函数对象本身的引用,这有利于匿名
函数的递归或者保证函数的封装性。 下面一段代码先说明callee的用法,实例代码摘自网上 :
function calleeLengthDemo(arg1, arg2) {
alert(arguments.callee.toString());
if (arguments.length == arguments.callee.length) {
window.alert("验证形参和实参长度正确!");
return;
} else {
alert("实参长度:" + arguments.length);
alert("形参长度: " + arguments.callee.length);
}
}
calleeLengthDemo(1);
func1().func2().func3.func4()
第一个消息框弹出calleeLengthDemo函数本身,这说明callee就是函数本身对象的引用。
callee还有个非常有用的应用就是用来判断实际参数跟行参是否一致。上面的代码第一个消息框会弹出实际参数的长度为1,形式参数也就是函数本身的参数长度为2.
caller
caller是函数对象的一个属性,该属性保存着调用当前函数的函数的引用(指向当前函数的直接父函数)
返回一个对函数的引用,该函数调用了当前函数。
functionName.caller,functionName 对象是所执行函数的名称。
注意:
对于函数来说,caller 属性只有在函数执行时才有定义。 如果函数是由 Javascript 程序的顶层调用的,那么 caller 包含的就是 null 。
function caller() {
if (caller.caller) {
alert(caller.caller.toString());
} else {
alert("函数直接执行");
}
}
function handleCaller() {//caller ====> handle
caller();
}
function handle(){
handleCaller()
}
handleCaller();
caller();
第一个alert会弹出调用caller函数的调用者handleCaller,而第二个alert由于没有在其他函数体内调用,所以caller为null,就执行了 alert(“函数直接执行”);
应用场景:
callee的应用场景一般用于匿名函数
var fn=function(n){
if(n>0) return n+fn(n-1);
return 0;
}
alert(fn(10))
函数内部包含了对自身的引用,函数名仅仅是一个变量名,在函数内部调用即相当于调用
一个全局变量,不能很好的体现出是调用自身,这时使用callee会是一个比较好的方法
var fn=(function(n){
if(n>0) return n+arguments.callee(n-1);
return 0;
})(10);
alert(fn)
这样就让代码更加简练。又防止了全局变量的污染。 caller的应用场景 主要用于察看函数本身被哪个函数调用。
bind、call、apply区别
在JS中,这三者都是用来改变函数的this对象的指向的
总结三者的相似之处:
- 都是用来改变函数的this对象的指向的
- 第一个参数都是this要指向的对象
- 都可以利用后续参数传参
call
继承,把父类私有属性和方法,克隆一份一模一样的,作为子类私有的属性和方法。
function A() { // 一个函数有三种角色:1、普通函数(私有作用域)2、类(new)3、普通对象(__proto__)
this.x = 100;
this.y = 200;
this.a = function () {
console.log(this.x);
}
}
A.prototype.getX = function () {
console.log(this.x);
};
function B() {
this.y = 100;
// this->b
//A()
A.call(this,aa,bb);// ->A.call(b) 把A执行,让A中的this变为了B
this.A(aa,bb)
//此时的A.prototype对B类来说是没用的,因为B没有继承A
}
var b = new B;
console.log(b.x); // ->100
b.a(); // ->100
console.log(b.y); // ->200
apply
apply和call方法的作用是一模一样的,都是用来改变方法中this关键字并且将方法执行,跟call唯一的的区别就是语法的区别:
//call传递参数是用逗号分隔,一个一个传进去
fn.call(obj, arg1,arg2,arg3.....)
//apply传递参数是用一个数组
fn.apply(obj, [arg1,arg2,arg3....])
bind
这个方法在IE6-IE8下不兼容。bind也是改变this的指向,但是对象在bind之后不会直接执行,需再次调用。
var obj = {num:1};
function fn(num1, num2) {
console.log(num1+num2);
}
fn(200,300)
fn.call(obj, 100, 200);//->输出300
fn.bind(obj, 100, 200); // 只是改变了fn中的this为obj,并且给fn传递了两个参数值,但是此时并没有执行fn这个函数。
//要让fn这个函数执行,下面的写法就行。
var myFn = fn.bind(obj, 100, 200);
newFn<====obj.fn(100,200)
newFn()
// obj={
//newfn:fn
//}
myFn();
//这是因为执行bind会有一个返回值,这个返回值myFn就是我们把fn的this改变后的那个结果!
区别:
call和apply都是对函数的直接调用,而bind方法返回的仍然是一个函数,因此后面还需要()来进行调用才可以
call后面的参数与say方法中是一一对应的,而apply的第二个参数是一个数组,数组中的元素是和say方法中一一对应的,这就是两者最大的区别。
作用域链
简单来讲,作用域(scope)就是变量访问规则的有效范围。
- 作用域外,无法引用作用域内的变量;
- 离开作用域后,作用域的变量的内存空间会被清除,比如执行完函数或者关闭浏览器
- 作用域与执行上下文是完全不同的两个概念
JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定。执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。
作用域链
在JS引擎中,通过标识符查找标识符的值,会从当前作用域向上查找,直到作用域找到第一个匹配的标识符位置。就是JS的作用域链。
什么是IIFE?有什么好处
IIFE就是立即执行函数表达式(Immediately-Invoked Function Expression)
1.创建块级(私有)作用域,避免了向全局作用域中添加变量和函数,因此也避免了多人开发中全局变量和函数的命名冲突;
2.IIFE中定义的任何变量和函数,都会在执行结束时被销毁。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了;