JS函数重新学习

函数又叫方法。
学了一段时间JS了,首先,我要如何看待函数?
自己要建立什么观念呢?其实刚开始我本人的做事方法是一口吃个胖子,能一下写一秃噜代码,就一下写到自己所想的再前一步,当然在后面开发项目的时候,代码一写一长溜,后来就觉得不好看,代码太难看了,还有就是函数的复用了。
为什么要用函数?为什么?我经常看老师写代码,明明可以接着写下去的,结果呢,他直接把剩下的思路造了一个函数出来,他就说他喜欢这样,方便。单单方便一个词,这种思维就是一种把步骤封装起来,模块化的感觉,虽然刚开始我是觉得多此一举的,事实上,这是一种做事习惯,态度。事实证明,这样的习惯确实很好,代码漂亮了,其实思路更清晰了,这是关键。所以,我也要如此,有种写代码就要时刻准备着函数的品质的。
对函数本身应该如何看待呢?它是一段命令,构成里面有参数。第一点,就是参数的研究,逻辑上涉及到了传参,赋值,作用域,声明前置但赋值不前置,作用域链,甚至是原型与原型链的那个this。第二点,这个函数要执行的话,在时机上是不是同步的,后续的代码执行要不要必须在它完成之后再执行?比如定时器,node.js的读写文件,上次写无限轮播时的animate的用法,事件处理,还有ajax发请求要数据,数据到了做什么,常识就是回调函数,当然多了就成回调地狱了,后续会有更多的写法,如Promise。

函数的声明,调用

function  functionName(){}   //声明
functionName()     //调用,代码复用
var  a = new sum()     //构造函数,原型对象那里的知识的声明
var a = function(){}    //函数表达式,跟声明是有区别的

函数的参数

调用的时候,会传递进去参数的,所有函数内部都有个arguments对象,类数组对象,arguments[0],就是第一个参数。

function name(a){
  console.log(a)
}
等同于
function name(){
  var a = arguments[0]
  console.log(a)
}

参数传多了,多的就是undefined,少了就是默认的从左到右传进去。
强制限制参数数量:

function(x,y){
  if(arguments.length !==2){...}
}

设置默认参数,其实在上次写Jquary插件的时候,就用到了,那个动画效果中的

function(x,y){
x = x || 0
y = y || 1
return [x,y]
}

把这个arguments转化为数组,方法:

function toArr(arguments){
  return [].slice.call(arguments)      //原型的this的知识
}

没有重载

没有重载,一个函数就是一个函数,再写一遍就覆盖了,如何传递不同参数?
只能在函数内部用if判断参数是哪一个参数,再相应地做事。

函数返回值

跟console.log()区别开,console.log()也是一个函数,这个函数值是undifined。只是把结果展示给控制台。
如果写一个函数没有return,这个函数值就是undifined。
return 会终止这个函数运行,我在项目里加锁经常用的。

声明前置

废话不多说,声明的变量,函数,都会提前,提前到哪里?到它所在的块级元素的最前面,全局作用域下,比较好说,提前到最前面。如果是函数内,就是在整个函数内的最前面。
这里跟后面要学的封装呼应了,想来十几天前刚学封装的时候,经常出错,就是因为在封装的对象里,声明的变量落后于执行它的函数,因为它们不在一个范畴了哦。因为一个this惹的祸。
然后,我要说的点是,只是声明前置,赋值依旧在运行到它所在的那行代码的时候,值才成立的。

console,log(a)   //undifined  而不是报错,也不是3
var a = 3
function foo(){
console.log(tmp)     //undifined    变量已声明前置  
var x > -3
if(x < 0){               //if之流都是语句,不是函数。这个语句有个块内部环境,
                      //但是函数内变量作用域是整个函数。
var tmp = -x
}
console.log(tmp)     //3
}

fn()                             //报错了
var fn = function(){
console.log('fn,,,')
}
等同于
var fn
fn()      //fn不是一个函数
fn=function(){
console.log('fn,,,')
}

命名冲突

原理就是声明前置🏠后面的声明赋值覆盖。

var fn = 3
function fn(){}
console.log(fn)   //3
等同于
var fn
function fn(){}
fn = 3
console.loog(fn)


function fn(fn){
  console.log(fn)     //相当于前面有个 var fn = arguments[0] = 10
var fn = 3
console.log(fn)
}
fn (10)  // 10   3

递归

自己调用自己,
比如阶乘。
效率很低,思路简单,逻辑清晰。

function fact(n){
  if(n ===1){
return  1
}
return  n*fact(n-1)
}

作用域与上下文

首先,作用域的研究对象是函数里的变量有意义的地方,那一块叫作用域。而上下文的研究对象是调用当前代码的对象的引用,也就是那个恶心的this。一个是变量作为研究时出现的,一个是这行代码对应的对象this作为研究时出现的。十几天前第一次讲原型对象时,我错误地认为作用域就是this,但是作用域还有作用域链,又感觉怪怪的,而且没有this的属性方法。

作用域

一提它,就想变量。
全局的,就是声明在全局,局部的就是声明在函数体内。
全局变量一直存在,随时调用,修改。
局部的变量只在局部有作用,跟局部外一点关系都木有,也不会影响到外部的其他代码,也不会被外部所知道。每次的存在意义就是在调用函数时才有,而且每次都有不同的作用域,期间只能在作用域内赋值,求值,对值,不能访问作用域外的值。
JS里{}没有带来块级作用域。但ES6里有let,所以函数里是会有的。
if,for循环之类的语句等,定义的变量都是全局的,外边可以访问,这就是块级作用域在JS中无效,有效的话,就不能被语句外访问到定义的变量了。在函数里的话就是函数的全局变量了。


立刻执行的函数表达式

这是模拟块级作用域的实例,也是闭包的应用。
JS 中,一个函数定义的变量,函数执行后,变量会被销毁的,这样,后续再访问其中的变量就访问不到了。

(function(){})()     //对,括号部分相当于变量了,阻止被解析成声明
function(){}()    //错
var a =function(){}
a()   //对

this上下文

上下文,就是一个函数被new出来时候,或者函数是一个对象的属性或者方法的形式时,反正就不是函数表达式,也不是字面量形式,哎,这时候this就出现了。就是有对象这种类型参与时,上下文就出来了。this是什么东东?
this是调用这个函数时,这个函数所属的对象。

var obj = {
  haha:function(){
    console.log(this === obj)
  }
}
obj.haha()      //true

全局变量都是window这个对象的属性。

作用域链

JS是单线程语言,在浏览器同时,只能做一件事。初始,默认作用域是全局window。执行函数,先创建它的作用域,是把函数作用域拉到作用域链的顶部,执行。执行完了,就移除,回到以前的作用域,以前的作用域可以是它父亲的作用域,更外边最外边就是全局。

每执行一个函数,就出在一个新作用域下。

作用域链的根本就是,从自己内部找变量,找不到就攀爬作用域链,从自己函数所声明的作用域找,找不到,从自己函数所在作用域代表的函数的作用域找,,,一层一层翻。还有一个注意点,声明前置。
练一练:

var a =1
function fn1(){
  function fn2(){
    console.log(a)       //找a,内部没有,从自己函数声明的那个作用域找
  }
  function fn3(){
    var a = 4
    fn2()
  }                 //找fn2
  var a = 2             //找到了,自身作用域有,没有找fn3里的。
  return fn3
}
var fn = fn1()    //找fn3
fn()   //2



var  a= 1
function fn1(){
  function fn3(){
    var a = 4
    fn2()
   }
  var a =2
  return fn3
}
 function fn2(){
    console.log(a)
  }
var fn = fn1()
fn()   //1  一个意思。fn2的作用域是全局。

这里调用一个函数fn,fn是全局变量,不会被删除,fn函数返回一个函数fn3,就生成一个闭包,保存了返回函数fn3的临时的变量。

闭包重新认知

当一个嵌套函数,在作用域外或者定义外被访问,让它可以在外部函数返回后被执行,就是闭包了。作用就是保存局部变量。

function ha(){
  var a =1
  return function say(){
    return a
  }
}

var haha = ha()
console.log(haha())   //1

保存变量对比:



这里让我想起了上次那个音乐主页做的封装的一个点了,因为要把这个主页分成两部分封装,但是两部分之间有个交互,需要从一个封装对象里传入另一个对象一些参数,并且保存。那里,直接在另一个封装对象里添加了一个属性,让这个属性的值等于参数,就保留了。不一样的想法了,,,

var man = (function (){
  var a =1
  function isman(x){
    a += x
  }
  function iswoman(x){
    a -= x
  }
  return{
    sex:'',
    sexmesold:function(x){
      //do something
    },
    sexget:function(x){
      isman(x)
    }
  }
})()        //立刻执行的变种可以理解为把var obj =man(),obj(),直接一步达到,也不引入新变量obj了。
//私有属性和方法,变量,都在它整个生命周期保留着,通过man.sexget与外部交互,这个sexget自己需求随意创建的,符合你的预期就行。
保护man的全局命名空间,很有用,暴露的接口就是那个回调函数了。

call和apply

所有函数都有这两个方法,只是参数不同。
call函数需要传递参数列表,apply函数允许你传递参数为数组:

function  ha(a,b,'c'){}
ha.call(window,a,b,'c')
ha.apply(window,[a,b,'c'])
//ha函数在window的作用域下执行,并提供了三个参数。为了复用函数,脱离它定义的作用域。只保留方法。

function ha(){
   
          console.log(this.name)

}
function me(){
  this.name = 'me'
ha.call(me)
}
me()  //'me'

function sum(){
}


还有一个bind,还是在原型那部分再写吧。
参考了大神的认识javascript中的作用域和上下文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • 继承 一、混入式继承 二、原型继承 利用原型中的成员可以被和其相关的对象共享这一特性,可以实现继承,这种实现继承的...
    magic_pill阅读 1,056评论 0 3
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,226评论 0 4
  • 1,javascript 基础知识 Array对象 Array对象属性 Arrray对象方法 Date对象 Dat...
    Yuann阅读 899评论 0 1
  • 2017年5月9日,《BMJ Open》杂志刊登的荟萃分析结果显示每日喝少量咖啡(含咖啡因和不含咖啡因的咖啡)可以...
    亨利福特健康阅读 201评论 0 0
  • 你老是自卷其中纠结朋友此时此刻是不是被你所占。 道理你都明白 随缘。 你老是关注朋友圈有没有人评论你关注你。 你是...
    老去惜花心阅读 96评论 0 1