js 深入学习(一)

this、原型、原型链、作用域、作用域链、闭包

this

this指的是函数运行时所在的环境, 随着执行环境的改变而改变。

var obj = {
  foo: function () { console.log(this.bar) },
  bar: 1
};

var foo = obj.foo;
var bar = 2;

obj.foo() // 1
foo() // 2

构造器thisreturn

  • 如果 return 返回的是一个对象,则返回这个对象,而不是 this。
  • 如果 return 返回的是一个原始类型,则忽略。
function Fn()  
{  
    this.user = 'qwe';  
    return {};  
}
var a = new Fn;  
console.log(a.user); //undefined

function Fn1()  
{  
    this.user = 'zxc';  
    return function(){};
}
var b = new Fn1;  
console.log(b.user); //undefined
function Fn()  
{  
    this.user = 'abc';  
    return 1;
}
var a = new Fn;  
console.log(a.user); //abc


function Fn()  
{  
    this.user = '123';  
    return undefined;
}
var a = new Fn;  
console.log(a); // {user: "123"}


function Fn1()  
{  
    this.user = '456';  
    return null;
}
var b = new Fn1;  
console.log(b.user); //456

箭头函数没有自己的this,this指向父级

原型,原型链

所有 JavaScript 对象都从原型继承属性和方法
Object.prototype 位于原型继承链的顶端

设置原型对象
  • obj.proto=prototypeObj.
  • Object.setPrototypeOf(obj, prototypeObj)。
  • Object.create(prototypeObj)。

在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。

遍历对象的属性时,原型链上的每个可枚举属性都会被枚举出来。要检查对象是否具有自己定义的属性,而不是其原型链上的某个属性,则必须使用所有对象从Object.prototype继承的hasOwnProperty方法。

function Person(name,age) {
		this.name = name;
  	this.age = age;
}
Person.prototype.getInfo = function() {
		return this.name + " is " + this.age;
}

var person1 = new Person('abc',17); //abc is 17
var person2 = new Person('def',18);  //def is 18
console.log(person1.getInfo());
console.log(person1.__proto__ == Person.prototype); // true
console.log(Person.prototype.constructor == Person);// true


作用域,作用域链

作用域

当前的执行上下文。作用域也可以根据代码层次分层,以便子作用域可以访问父作用域,通常是指沿着链式的作用域链查找,而不能从父作用域引用子作用域中的变量和引用。
全局作用域(全局上下文) 局部作用域(函数上下文) 块作用域(代码块,es6中let const)
当前作用域不存在的变量和引用,就沿着作用域链继续寻找

作用域链

作用域链保证对执行环境有权访问的所有变量和函数的有序访问
当前执行环境搜索不到所需变量或函数会搜索上一级作用域链

function compare(value1,value2) {
		if(value1>value2) {
    		return -1;
    } else if(value1<value2) {
    		return 1;
    } else {
    		return 0;
    }
}
var result = compare(5,10);


每个函数都有自己的执行环境

闭包

闭包

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。(MDN)

闭包是指有权访问另一个函数作用域中的变量的函数。(javascript高级程序设计)

function afun() {
    var a = 'a';
    function bfun() {
        console.log(a);
    }
    console.log([bfun]);
    console.dir(bfun);
}
afun();

在这里插入图片描述

内层函数引用外层函数的变量(词法环境,自由变量)就形成了闭包,return 这个内部函数只是为了便于使用内部函数,从而达到隐藏变量,不直接修改处理“私有变量”的目的。

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

词法环境 (所引用变量)为三个函数所共享

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
})();

console.log(Counter.value()); /* logs 0 */
Counter.increment();
Counter.increment();
console.log(Counter.value()); /* logs 2 */
Counter.decrement();
console.log(Counter.value()); /* logs 1 */

每个闭包都是引用自己词法作用域内的变量。

var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */
容易引发的问题

该问题并非由闭包引起。

function funFor() {
		var result = new Array();
  	
  	for(var i=0;i<10;i++) {
    		result[i] = function() {
        		return i;
        }
    }
  	return result;
}
funFor();

result返回函数数组,且[[Scopes]]中作用域链的第一位Closure(闭包)中i为10
函数中 i首先查找作用域链第一位中的i,当循环执行完后,i为10![image.png](https://img-blog.csdnimg.cn/img_convert/fb611690e2efb66a56ee80ca58f9934f.png#clientId=ue7cf8e92-6a30-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=619&id=NArO3&margin=[object Object]&name=image.png&originHeight=619&originWidth=737&originalType=binary&ratio=1&rotation=0&showTitle=false&size=38235&status=done&style=none&taskId=uea36c54f-2e51-4d47-a198-85499a74a85&title=&width=737)
这种影响可通过es6中let关键字来消除,或是通过使用更多的闭包来消除。

补充

构造器

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");

alert(user.name); // Jack
alert(user.isAdmin); // false

当一个函数被使用 new 操作符执行时,它按照以下步骤:

  1. 一个新的空对象被创建并分配给 this。
  2. 函数体执行。通常它会修改 this,为其添加新的属性。
  3. 返回 this 的值。

如果没有参数,可以省略 new 后的括号

new.target
function User(name) {
  if (!new.target) { //没有通过 new 运行
    return new User(name); // 
  }

  this.name = name;
}

let john = User("John"); // 将调用重定向到新用户
alert(john.name); // John
构造器的return

通常,构造器没有 return 语句。它们的任务是将所有必要的东西写入 this,并自动转换为结果。
但是,如果这有一个 return 语句,那么规则就简单了:

  • 如果 return 返回的是一个对象,则返回这个对象,而不是 this。
  • 如果 return 返回的是一个原始类型,则忽略。

执行环境(执行上下文)

执行环境

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。(无法直接访问)
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。

在这里插入图片描述
在这里插入图片描述

全局执行上下文

全局执行上下文只有一个,在客户端中一般由浏览器创建=>window对象

window对象还是var声明的全局变量的载体

函数上下文

每当一个函数被调用时都会创建一个函数上下文

同一个函数被多次调用,都会创建一个新的上下文

执行上下文栈用于存储代码执行期间创建的所有上下文

执行环境和作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中时不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。

循环中的函数表达式

利用循环,函数表达式来创建多个函数时,函数中的循环变量指向函数本身的父级作用域。

var result = new Array();

for(var i=0;i<10;i++) {
		result[i] = function() {
    		return i;
  	}
}
console.log(result)

在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值