ES5复习

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中定义的任何变量和函数,都会在执行结束时被销毁。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了;

闭包

上一篇:javaScript ES5常考面试题总结


下一篇:JS中所有数组的方法和所有的对象方法,包含ES5和ES6