目录

  • 1. 实现链式操作
  • 2. 对象
  • 2.1 通过[]访问属性名
  • 2.2 对象枚举
  • 2.2.1 遍历数组
  • 2.2.2 遍历对象
  • 2.3 hasOwnProperty
  • 2.4 instanceof ⭐⭐
  • 2.4.1 例子
  • 2.4.2 判断是否是数组的4种方式
  • 2.4.3 让 arr 调用 Object 上的 toString
  • 2.5 Object.defineProperty
  • 3. this
  • 3.1 普通函数中的 this
  • 3.2 构造函数中的 this
  • 3.3 call, apply
  • 3.4 总结
  • 4. callee, caller
  • 4.1 callee
  • 4.2 caller
  • 5. 面试题


1. 实现链式操作

每个函数return this

var sched = {
  wakeup: function(){
    console.log('Running');
    return this;
  },
  morning: function(){
    console.log('Go shopping');
    return this;
  },
  noon: function(){
    console.log('Having a rest');
    return this;
  },
  afternoon: function(){
    console.log('Studying');
    return this;
  },
  evening: function(){
    console.log('Walking');
    return this;
  },
  night: function(){
    console.log('Sleeping');
    return this;
  }
}
sched.wakeup().morning.().afternoon().evening().night();

2. 对象

2.1 通过[]访问属性名

[] 里面放字符串

var myLang = {
  No1: 'HTML',
  No2: 'CSS',
  No3: 'JavaScript',
  myStudingLang: function(num){
    console.log(this[`No${num}`]);
  }
}

myLang.myStudingLang(1);

最早的 JS 引擎 都是通过 obj[‘name’] 访问的,

后来有了 . 语法,实际上访问 obj.name ,内部隐式的转换为 obj[‘name’](chrome v8 引擎不一定是这么做的)

2.2 对象枚举

枚举:一组有共同特性的集合

JavaScript 的枚举实际上就是对象,有枚举就有遍历,是相辅相成的。

枚举 -> 遍历:在一组信息,按顺序一个一个的去获取其信息的过程

for-in 既可以遍历对象,也可以遍历数组

2.2.1 遍历数组

var arr = [1, 2, 3, 4, 5];

// 这个循环的过程就叫遍历的过程
// 方式1
for(var i = 0; i < arr.length; i++){
  console.log(arr[i]);
}

// 方式2
for(var i in arr){ 
  console.log(arr[i]);
}

2.2.2 遍历对象

var car = {
  brand: '马自达', // key : value
  color: 'white',
  displacement: '2.0'
}

for(var key in car){
  console.log(key + ': ' + car[key]);
  
  // car.key 不行原因: car.key -> car['key'] -> undefined
  console.log(car.key); // undefined
  
}

2.3 hasOwnProperty

功能:排除原型上的自定义属性

var obj = {
  name: 'Tom',
  age: 30
}

obj.hasOwnProperty('name') // true

例1

function Car(){
  this.brand = '马自达';
  this.color = 'white';
  this.displacement = '2.0';
}

Car.prototype = {
  lang: 5,
  width: 2.5
}

Object.prototype.name = 'Object';

var car = new Car();
console.log(car);

for(var key in car){ // 原型链上的自定义属性也会被打印出来
  // 找对象自身的属性,而不找原型上的所有属性
  if(car.hasOwnProperty(key)){ 
    console.log(key + ': ' + car[key]);
  }
}
var car = {
  brand: '马自达',
  color: 'white',
}

// 隐式的在找 car['displacement']
console.log('displacement' in car); // false

hasOwnProperty 是排除原型的,但是 in 是不排除的

function Car(){
  this.brand = '马自达';
  this.color = 'white';
  this.displacement = '2.0';
}

Car.prototype = {
  displacement: '3.0'
}

var car = new Car();
console.log('displacement' in car); // true

2.4 instanceof ⭐⭐

判断对象是否是该构造函数实例化出来的

A 对象的原型里到底有没有B构造函数的原型,在原型链上,只要B实例化出来的对象,它的原型和A对象原型链上重合的都是true

2.4.1 例子

function Car(){}
var car = new Car();

function Person(){}
var p = new Person();

console.log(car instanceof Car); // true
console.log(p instanceof Car); // false
console.log(car instanceof Object); // true
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true
console.log({} instanceof Object); // true

2.4.2 判断是否是数组的4种方式

var a = [];

// 1.
console.log(a.constructor); 

// 2. 
console.log(a instanceof Array);

// 3.
var str = Object.prototype.toString, // 缓存方法
    trueTip = '[object Array]';
str.call(a) === trueTip; // call 更改 this 指向
console.log(str); // [object Array]

分析:
Object.prototype = {
  toString: function(){
    this.toString(); // call(a) 就相当于 a.toString
  }
}

// 4. 
Array.isArray(a); // true

2.4.3 让 arr 调用 Object 上的 toString

var arr = new Array(1, 2, 3);
console.log(arr); // [1, 2, 3]
console.log(arr.toString()); // 1, 2, 3

// 让 arr 调用 Object 上的 toString
Object.prototype.toString.call(arr); // [object Array]

2.5 Object.defineProperty

实现下面条件,打印内容⭐

if(a == 1 && a == 2 && a == 3) {
  console.log('You win!!');
}

答:相等性判断 会经过 toString

// 方法1
var a = {
  _a: 0,
  toString() {
    return ++this._a;
  }
};
if(a == 1 && a == 2 && a == 3) {
  console.log('You win!!');
}

变形:

// get set -> getter setter

// window -> a

var _a = 0;
Object.defineProperty(window, 'a', {
  get(){
    return ++_a;
  }
})

if(a === 1 && a === 2 && a === 3) {
  console.log('You win!!');
}

变形:

var obj = {
  _a: 0,
  get a(){ // 语法糖
    return ++this._a
  }
}

// 或者
var obj = {
  _a: 0
}
Object.defineProperty(obj, 'a', {
  get(){
    return ++obj._a;
  }
})

if(obj.a === 1 && obj.a === 2 && obj.a === 3) {
  console.log('You win!!');
}

3. this

3.1 普通函数中的 this

  1. 在普通函数中,只要没有实例化这个函数, this 是默认指向 window 的;
  2. this 在全局范围内也是指向 window 的
// 函数内部的 this
function test(b) {
  this.d = 3; // 相当于 window.d = 3,相当于直接 d = 3
  var a = 1;
  function c(){}
}

test(123);

console.log(d); // == this.d == window.d

分析:
AO = {
  arguments: [123], // 形参和实参一一对应的时候会存进 arugments 中
  this: window, // 预编译的时候 this 默认指向 window
  b: undefined -> 123, 
  a: undefined,
  c: function c(){}
}

1. 在普通函数中,只要没有实例化这个函数, this 是默认指向 window 的;
2. this 在全局范围内也是指向 window 的

3.2 构造函数中的 this

构造函数的 AO,GO

function Test(){
  // var this = {
  // 	__proto__: Test.prototype
  // }
  this.name = '123';
}

var test =  new Test();

分析:
AO = {
  this: window ->
      {			// 实例化的时候被覆盖了
        name: '123',
        __proto__: Test.prototype,
      }, // 这就形成了原型链
}

GO = {
  Test: function Test(){...},
  test: {
    name: '123',
    __proto__: Test.prototype,
  }
}

3.3 call, apply

function Person(){
  this.name = 'Tom';
  this.age = 18;
}

function Programmer(){
  Person.apply(this);
  this.work = 'Programming';
}

var p = new Programmer();
console.log(p);

3.4 总结

  1. 全局 this -> window
  2. 预编译函数 this -> window
  3. apply,call 改变 this 指向
  4. 构造函数的 this 指向实例化对象

4. callee, caller

4.1 callee

arguments.callee 实参列表当中的一个属性

function test(a, b, c){
  // 打印的是正在被执行的函数对象
  console.log(arguments.callee); // arguments 实参所指向的函数是谁,就打印谁
  // 通过 arguments.callee 找到对应的函数,然后再打印它的长度,
  // 相当于打印 test.length
  console.log(arguments.callee.length);
  console.log(test.length); // 形参的长度
  console.log(arguments.length); // 实参的长度
}

test(1, 2, 3)

arguments.callee 在哪个函数就指向哪个函数

function test1(){
  // arguments.callee 在哪个函数就指向哪个函数
  console.log(arguments.callee); 
  
  function test2(){
    console.log(arguments.callee);
  }
  
  test2()
}

test1();

例子:用递归的方式累加 n 位数

function sum(n) {
  if(n <= 1) {
    return 1;
  }
  return n + sum(n - 1);
}

var res = sum(10);
console.log(res);


// 用立即执行函数实现
var sum = (function(n){
  if(n <= 1) {
    return 1;
  }
  return n + arguments.callee(n - 1);
})(10);

console.log(sum);

4.2 caller

'use scrict';
test1();
function test1(){
  test2();
}
function test2(){
  // 返回当前被调用函数的函数引用
  console.log(test2.caller); // 调用当前函数的函数引用
}

5. 面试题

  1. 打印结果是什么
function foo(){
  // 传 null ,没有改变 this 指向
  bar.apply(null, arguments); // 相当于执行 bar(arguments)
}

function bar(){
  console.log(arguments);
}

foo(1, 2, 3, 4, 5);
// [1, 2, 3, 4, 5]

所有的函数执行都有一个 call ,
如bar() -> bar.call(arugments)-> bar(arguments)
  1. JS 的 typeof 可能返回的值有哪些?
七种
object, boolean, string, number, undefined, function, symbol

注意: typeof(null) = object
  1. 执行结果是什么?
function b(x, y, a){
  arguments[2] = 10;
  alert(a); // 10
}

b(1, 2, 3);


function b(x, y, a){
  a = 10;
  alert(arguments[2]); // 10
}

b(1, 2, 3);

实参和形参是映射关系,一一对应
  1. 打印结果?
var f = (
  function f() {
    return 1;
  },
  function g() {
    return 2;
  }
)

console.log(typeof f); // function g(){..}

var f = (
  function f() {
    return 1;
  },
  function g() {
    return 2;
  }
)()

console.log(typeof f); // 'number'
  1. 结果
undefined == null; // true
undefined === null; // false
isNaN('100');	// false
parseInt('1a') == 1; // true
  1. 打印结果
function isNaN1(num){
  var res = Number(num);
  // NaN 不等于任何东西,包括它本身
  if(res == NaN){
    return true;
  } else {
    return false
  }
}
console.log(isNaN1('abc')) // false

// ----- 改写 -----
function isNaN1(num){
  var res = Number(num) + '';
  // NaN 不等于任何东西,包括它本身
  if(res == 'NaN'){
    return true;
  } else {
    return false
  }
}

console.log(isNaN1('abc')) // true
  1. 执行结果:{} == {},是否相等,为什么,怎么做?⭐
{} == {}; // false ⭐
因为引用值对比的是地址,两个空对象保存在不同的地址中

修改:
先声明一个对象,再把这个对象赋值给新对象,这个时候它们就相等了
var obj = {};
var obj1 = obj;
obj == obj1;
  1. 打印结果是什么?
var a = '1';
function test(){
  var a = '2';
  this.a = '3';
  console.log(a);
}

test(); // '2'
new test(); // '2'
console.log(a); // '3'

AO = {
  a: undefined -> '2',
  this: -> window 
        -> {
          a: 3,
          __proto__: test.prototype
            }
}

GO = {
  a: undefined -> 1 -> 3,
}
  1. 打印结果是什么?
var a = 5;
function test(){
  a = 0;
  console.log(a);
  console.log(this.a);
  var a;
  console.log(a);
}

test(); // 0 5  0
new test(); // 0 undefined 0