JavaScript 作用域、闭包、this和原型对象!!!

有你不知道的JavaScript

作用域

理解作用域,首先需要理解程序的运行过程中的几个重要角色

  • 引擎

    从头到尾负责整个JavaScript程序的编译及执行过程

  • 编译器

    负责语法解析、代码生成

  • 作用域

    负责收集并维护由所有声明的变量组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限

下面举个小栗子来说明三者之间的联系

var a = 2

​ 运行这行代码时会执行两个操作:首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后再运行阶段引擎会在作用域中查找该变量,如果能找到就会对其赋值(未找到赋值为undefind)。

词法作用域和动态作用域

词法作用域:词法作用域就是定义在词法阶段的作用域,或者说是,词法作用域是由你写代码时将变量和块作用域写在哪里决定的,因此当词法分析分析器处理代码时会保持作用域不变。

动态作用域:动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心在何处调用,或者说是,作用域链是基于调用栈的,而不是代码中的作用域嵌套。

JavaScript使用的是词法作用域

函数作用域

含义:属于这个函数的全部变量都可以在整个函数范围内使用及复用

“隐藏”变量和函数
function foo(a) {
    var b;
    b = a + bar(a * 2);
    function bar (a) {
        return a + 1;
    }
    return b
}

foo (2)

b // undefind
bar() // undefind

// 全局变量中无法访问函数内部变量与函数
规避冲突

立即执行函数(IIFE)
// 避免变量污染全局
var a = 2;

(function IIFE (glob) {
	var a = 3
	log( a ) // 3
	log( glob.a ) // 2
})(window)
// 解决undedind被覆盖
undefind = true

(function IIFE ( undefind ) {
	var a;
     if (a === undefind) {
         log( "a is undefind" )
     }
	
})()
// 倒置代码运行顺序
var a = 2;

(function IIFE ( def ) {
	def (window)
})(function (glob) {
    var a = 3
	log( a ) // 3
	log( glob.a ) // 2
})
块作用域
“假”块级作用域
var foo = true
if (foo) {
    var bar = foo * 2
}
console.log('bar', bar)	// 仍然能访问 bar ,var 声明的变量写在哪里都是一样的,这仅仅只是看起来像是块级作用域,将var修改为let则会访问不到bar
try/catch块作用域

ES3中规定的 try/catch 会创建块级作用域

try {
	undefind()
} carch (err) {
	log (err) // 能正常执行
}

log(err) // err not find
let
闭包

当函数可以记住访问所在的词法作用域时,就产生了闭包。

function foo () {	var a = 2;		function bar () {		log (a)	}		bar()}foo() // 2 闭包的作用
应用
循环和闭包
for (var a = 0; i < 5; i++) {	(function(index) {		setTimeout(function() {			log( index )	// 0, 1, 2, 3, 4	闭包的作用		}, 10)	})(i)}
模块封装
function CoolModule () {    	var something = 'cool'    var another = [1, 2, 3]        function doSomething() {        log(something)    }    function doAnother() {        log(another)    }        return {        doSomething,	// 只对外暴露两个接口,隐藏内部变量        doAnother    }}

模块模式必须具备两个条件:

  • 必须有外部的封闭函数,该函数必须只被调用一次(每次调用都会被创建一个新的模块实例)
  • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或者修改私有的状态
this

当一个函数被调用时,会创建一个活动记录(上下文)。这个记录会包含函数在哪被调用(调用栈)、函数的调用方式、传入的参数等信息。this在函数执行过程中用到,它与绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

绑定规则
默认绑定

独立的函数调用,无法使用其他规则时的默认规则

function foo() {    log(this.a)		}var a = 2foo()	// window
隐式绑定

调用的位置 是否有上下文

function foo() {    log(this.a)  }var obj = {    a: 2,    foo: foo}obj.foo()	// obj

当函数引用有上下文时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象,或者是说,对象属性引用链中只有最后一层在调用位置起作用

显示绑定
function foo () {    log(this.a)}var obj = {    a: 2}foo.call(obj)	// obj
new绑定

使用 new来构造foo(…)时,会构造一个新对象并把它绑定到foo(…)调用中的this

function foo(a) {    this.a = a}var bar = new foo(2)log(bar.a)	// bar
优先级

new绑定 > 显示绑定 > 隐式绑定

new操作步骤
  • 构建一个全新的对象
  • 这个新对象会被执行[[prototype]]连接
  • 这个新对象会绑定到函数调用的this
  • 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
bind、call、apply区别
// 调用区别Function.call(obj[, param1[, param2[, …[, paramN]]]])Function.apply(obj[, argArray])Function.bind(thisArg[, arg1[, arg2[, ...]]])// bind 方法的返回值是函数,并且需要稍后调用,才会执行。而 apply 和 call 则是立即调用。
对象
属性描述符
var obj = {	a : 2}Object.getOwnPropertyDescriptor(obj, "a");------------结果-------------{    value: 2,    writable: true,			可写		是否可以修改值    enumerable: true,		可枚举		    configurable: true,  	可配置		}
configurable

true,能通过defineProperty()修改属性描述符,为false之后 ,不能再改回来,单向操作,并且还能 禁止 删除这个属性

对象常量

结合writable: falseconfigurable: false(不可修改、重定义或者删除)

禁止扩展

禁止对象添加新属性并且保留已有属性,可以使用Object.preventExtensions()

var obj = {a: 2}Object.preventExtensions(obj)obj.b  =  3log(obj.b) 	// undefind
密封

Object.seal()会创建一个密封对象,这个方法的实质是Object.preventExtensions()结合configurable: false,所以密封之后,所有属性不可扩展,不可重新配置或者删除现有的属性,但是可以修改属性值

冻结

Object.freeze()冻结一个对象,实际上调用Object.seal()并且结合writable: false,禁止对于对象本身及其任意直接属性进行修改(这个对象引用的其它对象还是不受影响的)

深度冻结

在冻结的基础上,遍历属性,为对象则递归冻结

常用方法
调用方法作用
Object.preventExtensions()阻止扩展
Object.seal()密封
Object.freeze()冻结
可枚举
调用方法作用
for…in…遍历
Object.keys()返回key数组
Objeck.asign()拷贝
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值