第十四章 函数的深扒
1. arguments
arguments是再函数作用域内存在的一个类数组对象,存储函数的参数等属性。
function foo(){
console.log(arguments);
for(var i = 0;i< arguments.length+2;i++){
console.log(arguments[i]);
}
}
foo(1,2,"哈哈哈",{},[1,2]);//
arguments参数只有在函数作用域里面才生效,它是一个对象,但是和数组一样可以通过[0]或者[1]的方式依顺序访问函数的参数,并且具有一个length属性,访问的是函数的参数的个数。
arguments里面还有属性callee,指的是函数本身,可以在函数内部调用函数自己(不推荐使用)
var sum = function(num){
if(num>0){
return arguments.callee(num-1)+num
}
return num
}
sum(100);//5050
这里不知道函数名字(匿名函数),但是还是依旧可以调用自身。
2. 剩余参数rest
现在我有一个需求:我需要一个函数,传入不定长参数,第一个参数是名字,第二个以后都是我的收益,函数返回名字加收益的总和
function add(name,...rest){
let sum = 0;
for(var i = 0;i<rest.length;i++){
sum+=rest[i];
}
return name+sum
}
add("pika",12,321,3,123,12,31,23);//"pia525"
将多余的实参传入rest数组中,如果没有多余的则rest数组为空
默认参数:
function Person(name="pika",sex="boy",age=18){
var o = {
"name": name,
"age": age,
"sex": sex,
}
return o;
}
let myperson = Person("pika");//{name:"pika",age:18,sex:"boy"}
let newperson = Person();//{name:"pika",age:18,sex:"boy"}
用于应对不定参数或者参数残缺的接口。
3. 箭头函数与function的区别
function foo(){//普通的函数声明
console.log(1)
}
var foo = () => console.log(1);//箭头函数
区别:
- 一个是function函数申明,一个是箭头函数函数表达式
- function里面有arguments,箭头函数没有arguments,用(…rest)代替
- 箭头函数没有构造函数,this指向是外部上下文的this(无法改变)。
4. this是什么,this在function函数里面的指向
this是"这个"的意思,this指向调用函数的主体对象。
console.log(this);//window,浏览器对象
//这意味着他和window.console.log等价
window.console.log === console.log;//true,浏览器对象调用console.log
在全局中this指向window
var name = "window name";
//全局的name是当window存在都不会消失,跳转也不会
var o = {
"name":"this object",
"getThis":function(){
console.log(this===o,this.name);
}
}
o.getThis();//true "this object"
此时getThis函数是o调用的,所以函数内部的this指向o
但是
var o = {
"name":"this object",
"getThis":function(){
console.log(this===o,this.name);
}
}
var name = "window name";
var fn = o.getThis;
fn();//false "window name"
为什么此时就变成了false了呢。因为fn获得是一个函数。现在调用fn()则变成了window.fn(),此时的this指向了window,调用函数的主体是window,this.name===window.name,变成了"window name"。
5. this在箭头函数里的指向,this的作用是什么
var name = "window name"
var o = {
"name":"this object",
fn:function(){
console.log(this===o);
//let that = this;
return ()=>{
console.log(this===that,this.name);
}
}
}
o.fn()//true
let foo = o.fn()
foo()//true "this object"
//此时o.fn()是箭头函数()=>{},this指向声明阶段的上下文,也就是o
在普通函数中this的绑定是在执行位置绑定
在箭头函数里面的this在声明阶段硬绑定到上下文中。
var name = "window name"
var o = {
"name":"this object",
fn:function(){
console.log(this===o);
let that = this;
return function(){
console.log(this===that,this.name);
}
}
}
o.fn()();//true false "window name"
//此时o.fn()是function,执行时this指向window
- 作用
绑定事件
let leftBtn = document.querySelector(".left");
leftBtn.onclick = function(){
this.style.backgroundColor = "red";//点击事件触发的主体是leftBtn
}
循环绑定
let lilist = document.querySelectorAll(".list li");
for(var i = 0;i < lilist.length;i++){
lilist[i].onclick = function(){
this.innerHTML = "666";
}
}
6. 函数的name与length属性
函数得name是函数名下面的属性,是函数的唯一标识符
function foo(){
console.log(arguments.callee.name)
}
foo();//"name"
var a = function(){
console.log(arguments.callee.name)
}
a();//"a"
var b = () =>{}
console.log(b.name);//
函数的name属性就是函数名
7. arguments的length
函数的arguments的length属性是指参数类数组中参数的长度
function foo(){
console.log(arguments.length)
}
foo(1,2,3,4,5);//5
8. this的指向修改call apply与bind的意义
函数得this得指向是可以修改得。我们需要用到某些特殊得原型得方法得时候会用到call,apply还有bind强行修改。
- call/apply
var name = 'window name';
var o1 = {
"name": "object name",
getThis:function(){
console.log(this.name);
}
}
var o2 = {
"name": "another name",
}
o1.getThis();//"object name"
var fn = o1.getThis;
fn();//"window name"
fn.call(o2);//"another name"
fn.apply(o2);//"another name"
fn.call(o1);//"object name"
此时call/aplly都会使函数执行,相当于强行修改了调用函数得主体对象,在你想要得对象上借用了别的方法。方法得一处书写,到处使用。
var o = {
"0":"123",
"1":"456",
"2":"789",
"length":3,
}
o.forEach(function(item){
console.log(item);
});//此处会报错,因为对象o没有forEach方法,这是数组得方法
//于是
[].forEach.call(o,function(i){
console.log(i)
})//打印"123" "456" "789"
Array.prototype.forEvery = function(cb){//不完全方法
for(var i =0;i<this.length;i++){
cb(this[i]);
}
}//自己定义了一个方法
[1,2,3,4].forEvery(function(i){
console.log(i);
})//1 2 3 4
[].forEvery.call(o,function(i){
console.log(i)
})//"123" "456" "789"
call,apply得唯一区别
fn.call(this,x1,x2,x3,x4);//函数原本参数是一个一个传进去得
fn.apply(this,[x1,x2,x3,x4]);//函数原本参数放到数组中传进去
- bind
bind方法同样也可以修改函数执行得主体对象,但是bind是硬绑定,是在执行得时候返回一个绑定完成之后得函数,可以再任意地方执行
var name = 'window name';
var o1 = {
"name": "object name",
getThis:function(){
console.log(this.name);
}
}
var o2 = {
"name": "another name",
}
var sayName = o1.getThis.bind(o2);//返回一个绑定好得函数,啥都不返回
sayName();//"another name"
sayName.call(o1);//"another name"已经改不了了
其实bind得内部实现是,用到了call,或者是apply
Function.prototype.mybind=function(obj,...arg1){
return function(...arg2){
return this.apply(obj,arg1.concat(arg2))
}
}//粗略版,返回一个函数,此函数执行返回原函数绑定到obj上得硬绑定,arg1,arg2是参数,不考虑原型链,错误, 兼容性等情况
原型得问题在面向对象讲。call/apply/bind问题再深入讨论
9.严格模式
可以不是函数开头可以加一句"use strict";让函数变成严格模式
- 不允许用with。
- 所有变量必须声明,赋值给未声明的变量报错,而不是隐匿创建全局变量。
- eval中的代码不能创建eval所在作用域下的变量、函数。而是为eval单独创建一个作用域,并在eval返回时丢弃。
- 函数中的特殊对象arguments是静态副本,而不像非严格模式那样,修改arguments或修改参数变量会相互影响。
- 删除configurable=false的属性时报错,而不是忽略。
- 对象字面量重复属性名报错。
- 禁止八进制字面量,如010(八进制的8)。
- 严格模式下eval、arguments变为关键字,不能用作变量名。
- 一般函数调用时(不是对象的方法调用,也不使用apply/call/bind等修改this)this指向null,而不是全局变量。
- 试图修改不可写属性(writable=false),在不可扩展的对象上添加属性时报TypeError,而不是忽略。
- arguments.caller,arguements.callee被禁用