你不知道的javascript系列学习(this和对象原型)

二、this和对象原型

1、关于this

(1)、this到底是什么? 

this是在运行时进行绑定的,并不是在编写是绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数调用方式。

this既不指向函数自身也不指向函数的词法作用域。它指向声明完全取决于函数在哪里被调用。

2、this全面解析

(1)、调用位置

调用位置最重要的是要分析调用栈,调用位置就在当前正在只想函数的前一个调用中。

通过代码看看到底声明是调用栈和调用位置:

function baz(){

//当前调用栈是:baz

//因此,当前调用位置是全局作用域

console.log("baz");

bar();

}

function bar(){

//当前调用栈是baz ->bar

//因此,当前调用位置在baz中

console.log("bar");

foo();//<--foo的调用位置

}

function foo(){

//当前调用栈是baz ->bar->foo

//因此,当前调用位置在foo中

console.log("foo");

}

baz();//<--baz的调用位置

(2)、绑定规则

   (a)、默认绑定(独立函数调用)

如下代码:

function foo(){

console.log(this.a);// 

}

var a=2;

foo();//2 此时foo()是直接使用不带任何修饰的函数引用进行调用的,因此this是默认绑定,this指向全局变量。

   (b)、隐式绑定

隐式绑定规则会吧函数调用中的this绑定到这个上下文对象。它的落脚点指向调用的对象。

如:

function foo(){

console.log(this.a);

}

var obj2 = {

a:42,

foo:foo

}

var obj1 = {

a:2,

obj2:obj2

}

obj1.obj2.foo();// 42  此时this 指向最后一层调用他的对象obj2.所以输出42

另外一个常见的this绑定被隐式绑定的函数丢失绑定对象,会应用默认绑定,从而指向了全局对象,如果是严格模式指向undefied

如:

function foo(){

console.log(this.a);

}

var obj ={

a:2,

foo:foo

}

var bar = obj.foo();

var a = "opps,global";

bar();//"opps,global" bar 是obj.foo的一个引用,但实际上,它引用了foo函数本身。此时bar()等价于直接调用foo()函数,所以this应用了默认绑定规则,指向全局变量。

(c)、显示绑定

直接指定this的绑定对象,称之为显示绑定。通过call(..)或者apply(..)实现显示绑定。

如:

function foo(){

console.log(this.a);

}

var obj={

a:2

}

foo.call(obj);//2 通过foo.call(..),强制把它的this绑定到obj上。

(d)、new 绑定

   使用new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

   1、创建(或者说构造)一个全新的对象。

   2、这个新对象会被执行[[原型]]连接。

   3、这个新对象会绑定到函数调用的this.

   4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

   考虑下面的代码:

function foo(a){

this.a=a;

}

var bar = new foo(2);

console.log(bar.a);//2 

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

(3)、判断this的优先级,我们可以按照如下顺序来进行判断:

(a)、函数是否在new中调用(new绑定)?如果是的话,this绑定的是新创建的对象。

var bar = new foo();  //this 是bar对象

(b)、函数是否通过call,apply(显示绑定)?如果是的话,this绑定的是指定的对象。

var bar = foo.call(obj2); //this是obj2对象

(c)、函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。

var bar = obj1.foo(); //this是obj1对象

(d)、如果都不是的话,使用默认绑定,如果在严格模式下,就绑定到undefined,否则绑定到全局对象

var bar =foo(); //this 绑定到全局对象

3、对象

3.1 语法

对象可以通过两种形式定义:声明(文字)形式和构造形式。

(1)、对象的文字语法:

var myobj = {

key :value,

//

}

(2)、构造形式:

var myObj = new Object();

myObj.key = value;

两种方式生成的对象是一样的。

3.2 类型和内置对象

 javascript 六种主要类型(语言类型):string、number、boolean、null、undefined、object

 javascript 内置对象有:String、Number、Boolean、Object、Function、Array、Date、RegExp、Error

 对于string、number、boolean 引擎自动会把字面量转成相应的对象,可以访问其属性和方法。

如:42.359.toFixed(2);引擎把数字自动转成new Number(42.359).

 null和undefined没有对应的构造形式,他们只有文字形式。

3.3 对象内容

对象的内容是由一些存储在特定命名位置的(任意类型的)值组成的,我们称之为属性。

对象内容的访问可以通过“属性访问”或“键访问”方式。

如:

var myObject = {

a:2

}

myObject.a;//2 属性访问

myObject["a"];//2 键访问

       两种语法的区别在于:.操作符要求属性名满足标识符的命名规范,而[".."]键访问语法可以接受任意UTF-8/Unicode字符串作为属性名。

3.4 可计算属性名

可计算属性名是只属性名是动态的。可以在文字形式中使用[]包裹表达式来当做属性名:

如下代码:

var prefix = "foo";

var myObject = {

[prefix+"bar"]:"hello",

[prefix+"baz"]:"world"

}

myObject["foobar"];//hello

myObject["foobaz"];//world

3.5 属性描述符

ES5开始所有的属性都具备了属性描述符。如下代码:

var myObject = {a:2}

Object.getOwnPropertyDescriptor(myObject,"a");

//{value: 2, writable: true, enumerable: true, configurable: true}

普通对象属性还包括另外三个特性:writalbe(可写)、enumerable(可枚举)和configurable(可配置)

我们也可以使用Object.defineProperty(..)来添加或修改一个已有属性。

如:

var myObject={};

Object.defineProperty(myObject,"a",{

value:2,

writable:false,//不可写!

configurable:true,

enumerable:true

});

myObject.a=3;//写属性false,这里不能重新赋值

myObject.a;//2

(1)、禁止扩展

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

(2)、密封

Object.seal(..)会创建一个“密封”的对象,不能添加也不能配置或删除任何现有属性。

(3)、冻结

Object.freeze(..)会创建一个冻结对象,这样无法修改他们的值。

属性可以是可枚举或者不可枚举的,这决定了它们是否会出现在for .. in循环中。Object.keys返回一个对象属性值的数组。

4、原型

4.1 [[Prototype]]

javascript 中的对象有一个特殊的[[Prototype]]内置属性,其实就是对于其他对象的应用。

如果要访问对象中并不存在的一个属性,[[Get]]操作就会查找对象内部[[Prototype]]关联的对象,这个关联关系实际上定义了一条“原型链”,在查找属性时会对它进行遍历。

所有普通对象都有内置的Object.prototype,指向原型链的顶端,如果在原型链中找不到指定的属性就会停止。toString()、valueOf()和其他一些通用的功能都存在于Object.property对象上,因此语言中所有的对象都可以使用它们。

关联两个对象最常用的方法是使用new 关键词进行函数调用,在调用中会创建一个关联其他对象的新对象。

使用new 调用函数时会把新对象的.prototype属性关联到“其他对象”。带new的函数调用通常称为“构造函数调用”,尽管他们实际上和传统面向累语言中的类构造函数不一样。

对象之间关系不是复制而是通过内部的[[Prototype]]链关联,是一种委托。

5、行为委托

5.1 面向委托的设计

行为委托认为对象之间是兄弟关系,互相委托,而不是父类和子类的关系。

猜你喜欢

转载自zifeng412708.iteye.com/blog/2389373