ES5复习

这篇博客详尽地复习了JavaScript ES5中的关键概念,包括数据类型、数组、字符串、函数、事件处理和Cookie操作。深入探讨了JavaScript的typeof操作、数据类型之间的区别、数组方法如concat、slice和splice,以及字符串的方法如trim和模式匹配。还讲解了事件流模型、阻止事件冒泡和事件委托,以及函数的callee和caller属性,最后提到了IIFE及其作用。
摘要由CSDN通过智能技术生成

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,44,38,5,47,25,36,2,79,81];
    //定义循环次数,之前比较出的数值,不参与下一次的比较
    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 = [1,2,5,8,38,67];
    //外层循环,实现排序循环次数,次数是单元个数 -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 = {
   n:2,
   i:2
 }; 

 for (var i = 0, length = str.length; i < length; i++) {
  var char = str.charAt(i);
  
  if (object[char]) {
    //object[key]
   object[char]++; //次数加1
  } else {
   object[char] = 1; //若第一次出现,次数记为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 = '23598445646544'; 
var iNum = 10;
function test3(str) { 
  var iNum = str.length%3; //余数 2
  var prev = ''; 
  var arr = [23,598,445,646,544]; 
  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中定义的任何变量和函数,都会在执行结束时被销毁。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了;

闭包
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值