tabliu

大踏步走

JavaScript学习笔记(17)- JS中的this

JavaScript学习笔记。本笔记是基于在腾讯课堂《Web前端开发之JavaScript精英课堂【渡一教育】》课程学习过程中记录的一些提纲和关键点。
强烈推荐想要进行js入门学习来听听,尤其是前面姬成讲的基础知识点。

一、this的使用

函数预编译过程中this指向window

function test(c) {
  var a = 123;
  function b() {}
}

test(1);

//预编译环节
AO {
  arguments : [1],
  this : window,
  c : 1;
  a : undefined,
  b : function,
}

如果该函数是构造函数,在执行new test()过程中this会发生改变:

//this = Object.create(test.prototype);
//相当于
//{
//  __proto__ : test.prototype
//}
function test() {
  console.log(this);
}

test();

全局作用域中this指向window

callapply可以改变函数运行是的this指向

obj.func();func()里面的this指向ojb

var obj = {
  name : 'abc',
  a :  function() {
    console.log(this.name);
    console.log(this);
  }
}
obj.a();

对象里函数中的this指向:谁调用的这个方法,this就指向谁

var name = '222';
var a = {
  name : '111',
  say : function() {
    console.log(this.name);
  }
}
var fun = a.say;
fun();
//function() {
//  console.log(this.name); //this指向window,结果'222'
//}
a.say();
//function() {
//  console.log(this.name); //this指向a,结果'111'
//}
var b = {
  name : '333',
  say : function(fun) {
    fun();
  }
}
b.say(a.say);
//function() {
//  console.log(this.name); //this指向window,结果'222'
//}
b.say = a.say;
b.say();
//function() {
//  console.log(this.name); //this指向b,结果'333'
//}

其他知识点

arguments.callee

指向函数自身的引用。

举个栗子:求10的阶乘

var num = (function(n){
  if(n == 1) {
    return 1;
  }
  return n * arguments.callee(n-1);
})(10)

console.log(num);

一般的做法是定义一个实名函数fac(n),通过枚举的方法来循环调用fac(n) * fac(n-1);但如果是通过立即执行函数来实现,就无法循环调用函数名,只能通过argumengs.callee来调用自身。

function a() {
  console.log(arguments.callee);
  function b() {
    console.log(arguments.callee);
  }
  b();
}
a();

根据函数执行顺序从上到下执行,先执行function a()console.log(arguments.callee);;再执行function b()console.log(arguments.callee);。所以返回结果也是:function a(){}function b(){}

总结:arguments.callee返回的是当前执行环境下的函数体

func.caller

返回调用函数的函数体。

function a() {
  console.log(a.caller); //null
  b();

}
function b() {
  console.log(b.caller); //function a() { b(); }
}
a();

function a() {}函数没有任何调用;function b() {}function a() {}调用执行,所以返回结果:nullfunction a() { b(); }

总结:

  • 这两个方法经常会拿来一起比较,但实际开发中很少用到;
  • 在ES5语法中,这两个方法不可用。

三目运算符

条件判断 ? 是 : 否 并且会返回值

var num = 1 > 0 ? ('10' > '9' ? 0 : 1) : 1 - 1;
// 字符串相比是比的主位ask码
console.log(num); //1

课后练习

console.log打印结果?

var foo = 123;

function test () {
  this.foo = 234;
  console.log(foo);
}

test();

打印结果是:234

如果执行new test();的打印结果呢?

返回:123

运行test()new test()的结果分别是什么?

var a = 5;

function test () {
  a = 0;
  console.log(a);
  console.log(this.a);
  var a;
  console.log(a);
}

//test();
  // A0 {
  //   a : undefined;
  // }
  //a = 0;
  //console.log(a); //0
  //console.log(this.a); //5
  //var a;
  //console.log(a); //0

  //new test();
  // this == Object.create(test.prototype);
  // A0 {
  //   a : undefined;
  // }
  //a = 0;
  //console.log(a); //0
  //console.log(this.a); //undefined
  //var a;
  //console.log(a); //0

总结:

this考题涉及到的知识点:

  • 函数预编译(AO);
  • this指向问题;
  • 函数和构造函数的预编译差异。

课后作业

浅克隆

var obj1 = {
  name : 'abc',
  age : 123,
  sex : 'male',
  card : ['visa', 'life',['card1', 'card2']]
}

var obj2 = {}

function clone(origin, target) {
  var target = target || {};
  for(var prop in origin) {
    target[prop] = origin[prop];
  }
  return target;
}

clone(obj1, obj2);

浅克隆的方法很好实现,但也存在一个问题。如果对象的属性值是非原始值,那目标对象和原始对象都会指向到相同的索引上,改变一方,另一方也会发生改变。

现实项目中是不希望发生的,这样就需要用到深克隆的方法来实现。

深克隆

var obj1 = {
  name : 'abc',
  age : 123,
  sex : 'male',
  card : ['visa', 'life',['card1', 'card2']],
  wife : {
    name : 'bcd',
    son : {
      name : 'cdde'
    }
  }
}

var obj2 = {}

分析思路:

  • 遍历对象:for(var prop in obj)
  • 判断是否是原始值:typeof(obj[prop]) == Object
  • 判断是否是数组还是对象: constructor/instanceof/toString()三种方法,推荐使用toString()
  • 建立相应的数组或者对象:[] or {}
  • 递归

实现:

  var obj1 = {
    name: 'abc',
    age: 123,
    sex: 'male',
    card: ['visa', 'life', ['card1', 'card2']],
    wife: {
      name: 'bcd',
      son: {
        name: 'cdde'
      }
    }
  }

  var obj2 = {}

  function deepClone(origin, target) {
    var target = target || {},
      toStr = Object.prototype.toString,
      arrStr = '[object Array]';
    for (var prop in origin) {
      if (origin.hasOwnProperty(prop)) {
        if (origin[prop] !== 'null' && typeof (origin[prop]) == 'object') {
          if (toStr.call(origin[prop]) == arrStr) {
            target[prop] = [];
          } else {
            target[prop] = {}
          }
          deepClone(origin[prop], target[prop]);
        } else {
          target[prop] = origin[prop];
        }
      }
    }

    return target;
  }

使用三目运算符还看简化下代码:

  function deepClone(origin, target) {
    var target = target || {},
      toStr = Object.prototype.toString,
      arrStr = '[object Array]';
    for (var prop in origin) {
      if (origin.hasOwnProperty(prop)) {
        if (origin[prop] !== 'null' && typeof (origin[prop]) == 'object') {
          // if (toStr.call(origin[prop]) == arrStr) {
          //   target[prop] = [];
          // } else {
          //   target[prop] = {}
          // }
          target[prop] = toStr.call(originp[prop] == arrStr) ? [] : {};
          deepClone(origin[prop], target[prop]);
        } else {
          target[prop] = origin[prop];
        }
      }
    }

    return target;
  }

总结:

在实现深克隆功能的过程中一定要注意一下几个问题:

  1. Object.prototype.toString的使用;
  2. hasOwnProperty()的使用;
  3. =====的区别使用;
  4. typeof 的返回结果都是小写的字符串。

本节完。