网安知识点二

目录

一、js下new命令的用法

1.对象

(1)对象是单个实物的抽象。

(2)对象是一个容器,封装了属性(property)和方法(method)。

2.构造函数

3.new命令

(1)基本用法

 (2)new命令重要性

4.Object.create() 创建实例对象

二、this关键字

1.含义

2.实质

3.使用场合

(1)全局环境

(2)构造函数

(3)对象的方法

4.使用注意点

(1)避免多层 this

(2)避免数组处理方法中的 this

(3)避免回调函数中的 this

5.绑定this的方法

(1)Function.prototype.call()

(2)Function.prototype.apply()

(3)Function.prototype.bind()


一、js下new命令的用法


1.对象


面向对象编程将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。

而类是一类事物,比如学生就是一个类,如果学生这个类通过new这个关键词生成一个实例对象,这个实例对象就是一个具体的人。通常在类中都含有一个构造函数,构造函数可以传递相应的属性,类在实例化的瞬间构造函数会自动执行把属性赋值给实例对象。假设在学生类中有一个学生,在实例化时构造函数可以给他赋值姓名为张三年龄为18,即就是将抽象的一类事物具体到某一个对象上。

在js中不存在类的概念,可以将构造函数视为一个类。

(1)对象是单个实物的抽象。


一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个远程服务器连接也可以是对象。

(2)对象是一个容器,封装了属性(property)和方法(method)。


属性是对象的状态,方法是对象的行为(完成某种任务)。比如,我们可以把动物抽象为animal对象,使用“属性”记录具体是哪一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。

2.构造函数

面向对象编程的第一步,就是要生成对象。对象是单个实物的抽象。通常需要一个模板,表示某一类实物的共同特征,然后对象根据这个模板生成。

JavaScript 语言使用构造函数(constructor)作为对象的模板。所谓”构造函数”,就是专门用来生成实例对象的函数。它就是对象的模板,描述实例对象的基本结构。一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。

var Vehicle = function () {
  this.price = 1000;
};

上面代码中,Vehicle就是构造函数。为了与普通函数区别,构造函数名字的第一个字母通常大写。

构造函数的特点有两个:

函数体内部使用了this关键字,代表了所要生成的对象实例。

生成对象的时候,必须使用new命令。

3.new命令


(1)基本用法


new命令的作用,就是执行构造函数,返回一个实例对象。

var Vehicle = function () {
  this.price = 1000;
};

var v = new Vehicle();
v.price // 1000

上面代码中,new命令让构造函数Vehicle生成了一个实例对象保存在变量v中,这个实例对象从Vehicle中得到了price属性。new命令执行时,构造函数内部this代表新生成的实例对象,this.price代表实例对象具有price属性,值为1000。

 (2)new命令重要性


如果忘记使用new命令,构造函数则变成普通函数,也不会生成实例对象,this此时也代表全局对象。

var Student = function(name) {

   this.name = name

}

var Stu = Student('zhangsan')

console.info(Stu)

console.info(name)

控制台会返回:

undefined

zhangsna

原因:

由于忘记使用new命令,构造函数Student变成了一个普通函数,普通函数的调用是在全局调用,此时的this也指向全局,而全局中的顶层变量为window,window.name为'zhangsan';而出现undefined是由于构造函数被当成普通函数执行,但却没有普通函数应该具有的return返回值,所以zhangsan赋值过来时undefined。

4.Object.create() 创建实例对象


构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用Object.create()方法。

var person1 = {
  name: '张三',
  age: 38,
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  }
};

var person2 = Object.create(person1);

person2.name // 张三
person2.greeting() // Hi! I'm 张三.

上面代码中,对象person1是person2的模板,后者继承了前者的属性和方法。

二、this关键字


1.含义


this就是属性或方法“当前”所在的对象。

var person = {
  name: '张三',
  describe: function () {
    return '姓名:'+ this.name;
  }
};

person.describe()
// "姓名:张三"

上面代码中,this.name在describe方法中调用,describe方法所在的当前对象是person,因此this指向person,this.name就是person.name。 相当于person调用了describe,describe调用了this.name,this.name的大环境在person这个对象里,所以指向了person中的属性name。

2.实质


由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。

var f = function () {};
var obj = { f: f };

// 单独执行   this指向全局
f()

// obj 环境执行 this指向obj
obj.f()

实例:

var f = function(){

   console.log(x);

};

f()

此时结果为:

x is not defined

修改为:

var f = function(){

   console.log(x);

};

var x = ‘aaa’

f()

此时结果为:

aaa

原因:f()运行在全局环境下,x指向全局里的window,而window中x未定义,所以第一次为undefined,第二次全局定义了x,即可以输出aaa。

3.使用场合


(1)全局环境

全局环境使用this,它指的就是顶层对象window。

this === window // true
​
function f() {
  console.log(this === window);
}
f() // true


上面代码说明,不管是不是在函数内部,只要是在全局环境下运行,this就是指顶层对象window。

(2)构造函数

构造函数中的this,指的是实例对象。

var Obj = function (p) {
  this.p = p;
};


上面代码定义了一个构造函数Obj。由于this指向实例对象,所以在构造函数内部定义this.p,就相当于定义实例对象有一个p属性。this指的是o。

var o = new Obj('Hello World!');
o.p // "Hello World!"


(3)对象的方法

如果对象的方法里面包含this,this的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。

var obj ={
  foo: function () {
    console.log(this);
  }
};
​
obj.foo() // obj


上面代码中,obj.foo方法执行时,它内部的this指向obj。

但是,下面这几种用法,都会改变this的指向。

// 情况一
(obj.foo = obj.foo)() // window
// 情况二
(false || obj.foo)() // window
// 情况三
(1, obj.foo)() // window


上面代码中,obj.foo就是一个值。这个值真正调用的时候,运行环境已经不是obj了,而是全局环境,所以this不再指向obj。

可以这样理解,JavaScript 引擎内部,obj和obj.foo储存在两个内存地址,称为地址一和地址二。obj.foo()这样调用时,是从地址一调用地址二,因此地址二的运行环境是地址一,this指向obj。但是,上面三种情况,都是直接取出地址二进行调用,这样的话,运行环境就是全局环境,因此this指向全局环境。上面三种情况等同于下面的代码。

// 情况一
(obj.foo = function () {
  console.log(this);
})()
// 等同于
(function () {
  console.log(this);
})()
​
// 情况二
(false || function () {
  console.log(this);
})()
​
// 情况三
(1, function () {
  console.log(this);
})()

如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。

4.使用注意点


(1)避免多层 this


由于this的指向是不确定的,所以切勿在函数中包含多层的this。

var o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    }();
  }
}
​
o.f1()
// Object
// Window


上面代码包含两层this,结果运行后,第一层指向对象o,第二层指向全局对象,因为实际执行的是下面的代码。

var temp = function () {
  console.log(this);
};
​
var o = {
  f1: function () {
    console.log(this);
    var f2 = temp();
  }
}


一个解决方法是在第二层改用一个指向外层this的变量。

var o = {
  f1: function() {
    console.log(this);
    var that = this;
    var f2 = function() {
      console.log(that);
    }();
  }
}
​
o.f1()
// Object
// Object


上面代码定义了变量that,固定指向外层的this,然后在内层使用that,就不会发生this指向的改变。

事实上,使用一个变量固定this的值,然后内层函数调用这个变量,是非常常见的做法,请务必掌握。

JavaScript 提供了严格模式,也可以硬性避免这种问题。严格模式下,如果函数内部的this指向顶层对象,就会报错。

var counter = {
  count: 0
};
counter.inc = function () {
  'use strict';
  this.count++
};
var f = counter.inc;
f()
// TypeError: Cannot read property 'count' of undefined


上面代码中,inc方法通过'use strict'声明采用严格模式,这时内部的this一旦指向顶层对象,就会报错。

(2)避免数组处理方法中的 this


数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
  }
}
​
o.f()
// undefined a1
// undefined a2


上面代码中,foreach方法的回调函数中的this,其实是指向window对象,因此取不到o.v的值。原因跟上一段的多层this是一样的,就是内层的this不指向外部,而指向顶层对象。

解决这个问题的一种方法,就是前面提到的,使用中间变量固定this。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    var that = this;
    this.p.forEach(function (item) {
      console.log(that.v+' '+item);
    });
  }
}
​
o.f()
// hello a1
// hello a2


另一种方法是将this当作foreach方法的第二个参数,固定它的运行环境。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    }, this);
  }
}
​
o.f()
// hello a1
// hello a2


(3)避免回调函数中的 this


回调函数中的this往往会改变指向,最好避免使用。

var o = new Object();
o.f = function () {
  console.log(this === o);
}
​
// jQuery 的写法
$('#button').on('click', o.f);


上面代码中,点击按钮以后,控制台会显示false。原因是此时this不再指向o对象,而是指向按钮的 DOM 对象,因为f方法是在按钮对象的环境中被调用的。这种细微的差别,很容易在编程中忽视,导致难以察觉的错误。

为了解决这个问题,可以采用下面的一些方法对this进行绑定,也就是使得this固定指向某个对象,减少不确定性。

5.绑定this的方法


(1)Function.prototype.call()


函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

var obj = {};
​
var f = function () {
  return this;
};
​
f() === window // true
f.call(obj) === obj // true


上面代码中,全局环境运行函数f时,this指向全局环境(浏览器为window对象);call方法可以改变this的指向,指定this指向对象obj,然后在对象obj的作用域中运行函数f。

(2)Function.prototype.apply()


apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。

func.apply(thisValue, [arg1, arg2, ...])
apply方法的第一个参数也是this所要指向的那个对象,如果设为null或undefined,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加。

function f(x, y){
  console.log(x + y);
}
​
f.call(null, 1, 1) // 2
f.apply(null, [1, 1]) // 2


上面代码中,f函数本来接受两个参数,使用apply方法以后,就变成可以接受一个数组作为参数。

(3)Function.prototype.bind()


bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数。

v

ar d = new Date();
d.getTime() // 1481869925657
​
var print = d.getTime;
print() // Uncaught TypeError: this is not a Date object.


上面代码中,我们将d.getTime()方法赋给变量print,然后调用print()就报错了。这是因为getTime()方法内部的this,绑定Date对象的实例,赋给变量print以后,内部的this已经不指向Date对象的实例了。

bind()方法可以解决这个问题。

var print = d.getTime.bind(d);
print() // 1481869925657


上面代码中,bind()方法将getTime()方法内部的this绑定到d对象,这时就可以安全地将这个方法赋值给其他变量了。

你可能感兴趣的:(javascript,开发语言,ecmascript)