note_20:恶补javasrcript基础_2

8 篇文章 0 订阅

恶补javasrcript基础_2


参考



没办法写得很严谨很专业,有些表述可能会有问题,因为基本上都是自学的,见谅。


1. 闭包

javascript忍者秘籍:

简单地说,闭包(closure)是一个函数在创建时允许该自身函数访问并操作该自身函数之外的变量时所创建的作用域。换句话说,闭包可以让函数访问所有的变量和函数,只要这些变量和函数存在于该函数声明时的作用域内就行。

网上还有一种解释是:

  • 函数A的内部函数B被函数A外的一个变量 c 引用。
  • 当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。

闭包可以是返回一个函数。

// 来自博客园:让你分分钟理解 JavaScript 闭包
// 作者:一像素
function A(){
    function B(){
       console.log('Hello Closure!');
    }
    return B;
}
var C = A(); // 相当于C = B
C();// Hello Closure!

闭包不一定是拿到函数的引用,也可以拿到变量的引用。

// 一个简单的闭包
// 来自javascript忍者秘籍

var outerValue = 'ninja';
function outerFunction() {
	// outerFunction(){}拿到了outerValue的引用
	assert(outerValue == "ninja", "I can see the ninja."); // 输出"I can see the ninja."
}
outerFunction(); // 执行该函数

闭包不一定要返回一个函数,关键在于外部变量拿到内部函数的引用

// 不那么简单的闭包
// 来自javascript忍者秘籍

var outerValue = 'ninja';
var later;
function outerFunction() {
	var innerValue = 'samurai';
	function innerFunction() {
		assert(outerValue, "I can see the ninja."); // 输出"I can see the ninja."
		assert(innerValue, "I can see the samurai."); // 输出"I can see the samurai."
	}
	// later作为一个外部变量,但是拿到了内部函数innerFunction(){}的引用
	later = innerFunction; 
}
outerFunction(); // later = innerFunction
later();

2. 作用域和作用域链

上面闭包的执行过程涉及到作用域和作用域链的问题。
作用域分两种:全局上下文和执行期上下文

(1)全局上下文

变量声明:带var的
全局范围内的形参和变量声明、实参、函数声明

刚才的例子

// 来自博客园:让你分分钟理解 JavaScript 闭包
// 作者:一像素
function A(){
    function B(){
       console.log('Hello Closure!');
    }
    return B;
}
var C = A(); // 相当于C = B
C();// Hello Closure!

全局上下文:没有形参和实参,var C声明了C变量,function A(){}是函数声明

(2)执行期上下文

某个函数内部范围内的形参和变量声明、实参、函数声明

刚才的例子

// 不那么简单的闭包
// 来自javascript忍者秘籍

var outerValue = 'ninja';
var later;
function outerFunction() {
	var innerValue = 'samurai';
	function innerFunction() {
		assert(outerValue, "I can see the ninja."); // 输出"I can see the ninja."
		assert(innerValue, "I can see the samurai."); // 输出"I can see the samurai."
	}
	// later作为一个外部变量,但是拿到了内部函数innerFunction(){}的引用
	later = innerFunction; 
}
outerFunction(); // later = innerFunction
later();

outerFunction的执行期上下文:没有形参和实参,var innerValue声明变量innerValue,function innerFunction(){}是函数声明。

另一个例子

// 闭包可以访问到什么内容
// 来自javascript忍者秘籍

var outerValue = 'ninja';
var later;
function outerFunction() {
	var innerValue = 'samurai';
	function innerFunction(paramValue) {
		assert(outerValue, "Inner can see the ninja."); // 输出"Inner can see the ninja."
		assert(innerValue, "Inner can see the samurai."); // 输出"Inner can see the samurai."
		assert(paramValue, "Inner can see the wakizashi."); // 输出"Inner can see the wakizashi."
		assert(tooLate, "Inner can see the ronin."); // 输出"Inner can see the ronin."
	}
	later = innerFunction;
	var tooLate = 'robin';
	later('wakizashi'); // innerFunction的实参
}
assert(!tooLate, "Outer can't see the ronin."); // 输出"Outer can't see the ronin."
var tooLate = 'ronin';
outerFunction();
later('wakizashi');

innerFunction执行期上下文:形参paramValue,没有变量声明,实参值是’wakizashi’,没有函数声明

outerFunction执行期上下文:var innerValue声明变量innerValue,函数声明function innerFunction(){}

innerFunction全局上下文:outerFunction执行期上下文!

(3)作用域链

当函数进行预编译的时候,会生成全局上下文。
当函数准备执行的时候,会生成执行期上下文。作用域链上面各种上下文有排列顺序,该函数的执行期上下文在最顶端,它的全局上下文第二位,然后包含它的外部函数的全局上下文在第三位,以此类推。
如果要在这个函数里访问某些变量,会优先去它的执行期上下文寻找这些变量的值,如果没有就去它的全局上下文找。如果它的全局上下文都没有,那就去包含当前这个函数的外部函数的全局上下文找。如果一直都找不到,那就是undefined。

① 预编译的解释:4.JavaScript的预编译

② 找完形参、变量声明、实参、函数声明之后,就要确定一下他们的值。
按顺序:

  • 先找形参和变量声明,如果有重复,那么只写一个。全部设置为undefined。
  • 然后找到实参,将实参的值赋给形参。
  • 找到函数声明,如果函数名跟前面的形参或者变量声明重复了,那就把前面的覆盖掉,把函数体设置为函数名的值。

③ 回到前面的例子,innerFunction执行期上下文:

  • 找到形参paramValue,没有变量声明,所以paramValue的值设为undefined
  • 实参值是’wakizashi’,所以paramValue的值设为’wakizashi’
  • 没有函数声明
// 闭包可以访问到什么内容
// 来自javascript忍者秘籍

var outerValue = 'ninja';
var later;
function outerFunction() {
	var innerValue = 'samurai';
	function innerFunction(paramValue) {
		assert(outerValue, "Inner can see the ninja."); // 输出"Inner can see the ninja."
		assert(innerValue, "Inner can see the samurai."); // 输出"Inner can see the samurai."
		assert(paramValue, "Inner can see the wakizashi."); // 输出"Inner can see the wakizashi."
		assert(tooLate, "Inner can see the ronin."); // 输出"Inner can see the ronin."
	}
	later = innerFunction;
	var tooLate = 'robin';
	later('wakizashi'); // innerFunction的实参
}
assert(!tooLate, "Outer can't see the ronin."); // 输出"Outer can't see the ronin."
var tooLate = 'ronin';
outerFunction();
later('wakizashi');
1. assert(!tooLate, "Outer can't see the ronin."); tooLate这个变量在全局上下文里,而且赋值在下一句,所以值是undefined,加了个取反之后等于true,所以会输出"Outer can't see the ronin."。
2. assert(outerValue, "Inner can see the ninja."); outerValue是outerFunction的全局上下文,位于innerFunction的作用域链上的第三位,值是'ninja',所以是true,输出"Inner can see the ninja."。
3. assert(paramValue, "Inner can see the wakizashi."); paramValue是innerFunction的形参,在执行期上下文里值是'wakizashi',所以是true,输出"Inner can see the wakizashi."。
4. assert(tooLate, "Inner can see the ronin."); tooLate是outerFunction的全局上下文里,但是因为赋值完了才执行outerFunction和innerFunction,所以值为'robin',所以为真,输出"Inner can see the ronin."

3. 函数声明和函数表达式

(1)函数声明

形如function xxx() {...},function前面没有任何东西(+/-/=/括号),就是function,一定要有函数名xxx,函数体有没有东西都无所谓。

(2)函数表达式

JavaScript中函数声明与函数表达式的区别详解:函数表达式将函数定义为表达式语句(通常是变量赋值)的一部分。通过函数表达式定义的函数可以是命名的,也可以是匿名的。函数表达式不能以“function”开头(下面自调用的例子要用括号将其括起来)。

例如

var func = function(){};
+function(){}
-function xxx(){}

前面说作用域的时候,上下文要找是函数声明!!!变量声明!!!

4. 立即执行函数

大佬已经写得很完整了:(译)详解javascript立即执行函数表达式(IIFE)

5. 有个题

来自网易云课堂:web前端开发JavaScript精英课js

function fn(a) {
	console.log(a) ; // function a() {}
	var a = 123 ;
	console.log(a) ; // 123
	function a() {}
	console.log(a) ; // 123
	console.log(b) ; // undefined
	var b = function() {} // 这个是函数表达式!!!不是函数声明!!!
	console.log(b) ; // function() {}
	function d() {}
	c = 456; // 不是变量声明,没有var。这种形式意味着c作为全局变量。
	console.log(c); // 456
}
fn(1) ; // 实参1
  • 形参a,变量声明var a,变量声明var b。所以是{a: undefined; b: undefined}
  • 实参值为1,统一形参实参。所以是{a: 1; b: undefined}
  • 函数声明function a和function d,所以是{a: function a(){}; b: undefined, d: function d(){}}
  • 所以第一个console.log(a)输出function a() {}
  • var a因为之前已经拿到了,所以直接看=后面的值,是123。这时执行期上下文变成{a: 123; b: undefined, d: function d(){}}
  • 所以第二个console.log(a)输出123
  • function a(){}刚才已经拿到了,所以可以直接跳过
  • 所以第三个console.log(a)还是输出123
  • b在执行期上下文里值为undefined,所以第一个console.log(b)输出undefined。
  • var b因为之前已经拿到了,所以直接看=后面的值,是个函数function(){}。这时执行期上下文变成{a: 123; b: function(){}, d: function d(){}}
  • 所以第二个console.log(b)输出function(){}
  • c = 456相当于声明了一个全局变量c,值为456。但是是在执行这个语句时才声明的,预编译的时候根本读不到c这个变量,因为前面没有var
  • 所以console.log( c)是456

还有个题

function Foo() {
  getName = function() {
    console.log("1");
  };
  return this;
}

Foo.getName = function() {
  console.log("2");
};

var getName = function() {
  console.log("4");
};

function getName() {
  console.log("5");
}

console.log(Foo.getName()); // 2
// Foo.getName相当于
// var a = new Function("getName=function(){};this.getName=function(){};return this;");
// 当访问Foo.getName的时候相当于Foo.getName = a.getName
// 然后a会被销毁掉
// 所以输出2

console.log(getName()); // 4
// getName是个全局函数
// 在没有执行var getName = function(){console.log("4"))}之前
// getName还是function(){console.log("5");}
// 但是执行了var getName = function(){console.log("4"))}之后
// getName就变成了function(){console.log("4"))}
// 所以输出4

console.log(Foo().getName()); // 1
// 执行了Foo()之后return回来的this是window
// 因为调用这个Foo()的是window
// 又因为执行Foo()的时候,全局函数getName发生了变化
// Foo(){getName=function(){};}和Foo(){this.getName=function(){}}是两回事
// 第一个getName会作为全局上下文,第二个getName是Foo的执行期上下文
// 所以这时Foo().getName()相当于window.getName()就会输出1

console.log(getName()); // 1
// 这里相当于window.getName()
// 所以和上面一样,输出1

console.log(new Foo.getName()); // 2
// new Foo.getName()相当于new一个function(){console.log("2");}的副本
// console.log(new Foo.getName())的时候new出来的那个函数会自动执行
// 所以输出2

6. promise(待续)

Ajax工作原理及实例
ajax是什么:

ajax 的全称是Asynchronous JavaScript and XML,其中,Asynchronous 是异步的意思,它有别于传统web开发中采用的同步的方式。

ajax的原理:

Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。要清楚这个过程和原理,我们必须对 XMLHttpRequest有所了解。

Promise是什么?

一个 Promise 就是一个代表了异步操作最终完成或者失败的结果对象。
“承诺将来会执行”(不管成功或者失败)的对象在JavaScript中称为Promise对象。

(1)使用promise

(2)创建promise

(3)正常任务与微任务

正常情况下,JavaScript的任务是同步执行的,即执行完前一个任务,然后执行后一个任务。只有遇到异步任务的情况下,执行顺序才会改变。

这时,需要区分两种任务:正常任务(task)与微任务(microtask)。它们的区别在于,“正常任务”在下一轮Event Loop执行,“微任务”在本轮Event Loop的所有任务结束后执行。

① setTimeout()和Promise.then()的执行顺序:
  • setTimeout语句指定的是“正常任务”,即不会在当前的Event Loop执行。而Promise会将它的回调函数,在状态改变后的那一轮Event Loop指定为微任务。
  • 所以,Promise.then()会先执行,setTimeout()是后执行。
② 常见的正常任务
  1. setTimeout
  2. setInterval
  3. setImmediate
  4. I/O
  5. 各种事件(比如鼠标单击事件)的回调函数
③ 常见的微任务

微任务目前主要是process.nextTick和 Promise 这两种情况。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值