变量,对象,new,this,call,apply

211 阅读5分钟

函数声明和变量声明

  • const

用于声明常量。注意:定义的变量的时候,必须同时初始化,且其值之后不可以修改

  • var

var可以用来声明全局变量,也可以声明局部变量,依据它们声明的位置。若没有使用关键字声明的变量,默认为全局变量。全局变量很多,防覆盖,要想使用一个局部变量,我们就需要声明并执行一个函数。这就是立即执行函数。 暂时性死区

  • let

块级作用域{} 暂时性死区 不存在变量提升

使用var时这是因为i在for循环内部进行递增,
当for循环体的外部调用i时,i必须递增完成后才能调用。

暂时性死区
所谓的暂时性死区就是在变量声明之前调用他,
结果会返回undefined,不仅let有这个特性,var也有

不可重复声明
typeof a //error: a is not defined 
a = 20 //error: a is not defined 
typeof a //error: a is not defined 
let a = 10

变量提升

var v = "hello";
(function(){
  console.log(v);
  var v = "world";
})(); //undefined

js 没有块级作用域
var v = "hello";
if(true){
  console.log(v); // hello
  var v = "world";
  console.log(v);// world
}  
console.log(v);// world

如果有块级作用域,明显if语句将创建局部变量name,
并不会修改全局name,可是没有这样,所以Js没有块级作用域。

注意函数表达式并没有被提升

  f1(); //ReferenceError: f1 is not defined
  f2();
 
  var f1 = function(){};
  function f2(){}

函数提升优先变量提升

优先级
1、语言内置:所有的作用域中都有 this 和 arguments 关键字
2、形式参数:函数的参数在函数作用域中都是有效的
3、函数声明:形如function foo() {}
4、变量声明:形如var bar;
(function(){
    var foo;
    console.log(typeof foo); //function
     
    function foo(){}
 
    foo = "foo";
    console.log(typeof foo); //string    顺序执行 属于2形式参数
})();
f();//TypeError: f is not a function
foo();//ReferenceError: foo is not defined
var f = function foo(){console.log(typeof foo);};
f();//function
foo();//ReferenceError: foo is not defined
//命名函数表达式的名字只在该函数的作用域内部有效。

    function Foo() {
        getName = function () {
            console.log(1);
        }
        return this;
    }
    Foo.getName  = function () {
        console.log(2);
    }
    Foo.prototype.getName  = function () {
        console.log(3);
    }
    function getName () {
        console.log(4);
    }
    var getName = function () {
        console.log(5);
    }

    Foo.getName();              // 2
    getName();                  // 5
    getName();                  // 5
    new Foo.getName();          // 2
    new  Foo().getName();       // 3
    new new  Foo().getName();   // 3

    Foo().getName();    // TypeError

对象的声明 两种方式

创建对象

  • 混合工厂方式
  • 通过Object.setPrototypeOf创建对象。

new关键字的过程

1. 创建一个空对象,并且 this 变量引用该对象,同时还继承了该函数的原型
2. 属性和方法被加入到 this 引用的对象中
3. 新创建的对象由 this 所引用,并且最后隐式的返回 this

var obj  = {};
obj.__proto__ = Base.prototype;
Base.call(obj); 

谈谈this的理解

  1. this总是指向函数的直接调用者(而非间接调用者)
  2. 如果有new关键字,this指向new出来的那个对象
  3. 在事件中,this指向目标元素,特殊的是IE的attachEvent中的this总是指向全局对象window。

call与apply的区别

apply,call,bind一般用于指定this的环境,动态改变某个类的某个方法的运行环境。

var b = a.fn  
b()  //undefined
原因还是在于b和a的this的环境不同,b的this指向的是全局,并没有指向a

call第一个参数传入环境,其余参数可以进行传参。b.call(a,1,2)

apply的第一个参数传入环境,传入的其余参数必须为数组;b.apply(a,[1,2])

bind方法返回的是修改后的函数。 b.bind(a,1,2)

call和apply的第一个参数为null,那么this指向全局环境。

this指向

1.
obj.getName();//指向obj

2.普通函数内部的this分两种情况,严格模式和非严格模式。 
非严格模式下,this 默认指向全局对象window

getName();//非严格模式下,指向window,严格模式下为undefined

3.如果函数是一个对象的构造函数,this指向新对象。 因为new 改变了this的指向
var a = new A();
   a();//指向A本身

4.
getName().apply(obj);//指向obj

var obj={
	fn:function(){
		setTimeout(function(){
			console.log(this);
		});
	}
}
obj.fn();//window
这次this指向了最外层的window对象,
这次this出现在全局函数setTImeout()中的匿名函数里,
并没有某个对象进行显示调用,所以this指向window对象

var obj1={
	num:4,
	fn:function(){
		var f=() => {    
			console.log(this);
			//object,f()定义在obj1对象中,this就指向obj1,
			这就是箭头函数this指向的关键
			
			setTimeout(function() {
				console.log(this);
				//window,非箭头函数的情况下还是要看宿主对象是谁,
				如果没有被对象调用,函数体中的this就绑定的window上
			});
		}
		f();
	}
}
obj1.fn();
var o = {
    a:10,
    b:{
        fn:function(){
            console.log(this.a); //undefined
        }
    }
}
o.b.fn();

// 如果返回值是一个对象,那么this指向的就是那个返回的对象,
如果返回值不是一个对象那么this还是指向函数的实例。(null)
function fn()  
{  
    this.user = '追梦子';  
    return function(){};
}
var a = new fn;  
console.log(a.user); //undefined
  • 如果一个函数中运行了一个内联函数,比如一个事件监听器,则this指向内联函数的源代码。例如,当设置一个按钮的单击处理程序,this将引用匿名函数内的按钮。

  • 如果函数被定义在一个对象上,然后调用对象时,this指向该对象。

  • 在异步编程中,this可以很容易改变过程中一个功能操作。保持处理程序上下文的一个小技巧是将其设置到闭包内的一个变量,当在上下文改变的地方调用一个函数时,如setTimeout,你仍然可以通过该变量引用需要的对象。 操作this的另一种方式是通过call、apply和bind。

箭头函数中的this指向谁?

所谓的定义时候绑定,就是this是继承自父执行上下文中的this, 比如这里的箭头函数中的this.x,箭头函数本身与say平级以key:value的形式,也就是箭头函数本身所在的对象为obj, 而obj的父执行上下文就是window,因此这里的this.x实际上表示的是window.x (this只有在函数被调用,或者通过构造函数new Object()的形式才会有this)

注意:简单对象(非函数)是没有执行上下文的!