2020-08-27

JS this指向问题

1.全局环境中this指向全局变量(window);

2.函数中的this,由调用函数的方式来决定,

 (1)如果函数是独立调用的,在严格模式下(use strict)是指向undefined的,在非严格模式下,指向window;

 (2)如果这个函数是被某一个对象调用,那么this指向被调用的对象;

3.构造函数与原型里面的this
构造函数里的this以及原型里的this对象指的都是生成的实例;(由new决定的)

通过new操作符可以初始化一个constructor的指向,new的作用就是创建一个对象的实例,constructor也就指向了一个新的执行环境“在这个对象之中”;

4.箭头函数按词法作用域来绑定它的上下文,所以this 实际上会引用到原来的上下文。
(箭头函数会保持它当前执行上下文的词法作用域不变,而普通函数则不会,箭头函数从包含它的词法作用域中继承了this的值)。
  1. this默认指向全局作用域
this === window; // true

function test(){
  this.x = 1
  console.log(this.x)
}
test() // output : 1
  1. 作为对象方法调用
var obj = {
  name : 'Jack',
  f1 : function(){
    console.log(this.name)
  }
}
obj.f1() // output : Jack





var mathLib = {
    pi: 3.14,
    area: function(r) {
        return this.pi * r * r;
    },
    circumference: function(r) {
        return 2 * this.pi * r;
    }
};
mathLib.area(2); // 12.56





document.getElementById('div').onclick = function() {
    console.log(this) // this --> div
}
  1. 作为构造函数调用
function Test(){
  this.x = 1
}
var o = new test()
console.log(o.x) // output: 1
  1. 被匿名函数调用
    匿名函数的执行具有全局性,this指向window对象
var name = "The Window"
var obj = {
  name : 'The Object',
  getName: function(){
             return function(){
                        console.log(this.name);                  
                      }
               };
}
obj.getName()() // output : The Window
  1. 使用call()/apply()/bind()改变作用域
function Foo(){
  console.log(this.a)
}
var food = {a : magic food}
Foo.call(food) // output : magic food

Foo() // undefine {}.a 此时this指向全局




var x = 0;
function test(){
  console.log(this.x)
}
var obj = {}
obj.x = 1
obj.m = test
obj.m.apply() //0,apply()的参数为空时,默认调用全局对象
obj.m.apply(obj); //1



var cylinder = {
    pi: 3.14,
    volume: function(r, h) {
        return this.pi * r * r * h;
    }
};
cylinder.volume.call({pi: 3.14159}, 2, 6);//  output 75.39815999999999
var newVolume = cylinder.volume.bind({pi: 3.14159});
newVolume(2,6); // Now pi is 3.14159



  1. 例题
/*111111*/
var x = 3;
var y = 4;
var obj = {
    x: 1,
    y: 6,
    getX: function() {
        var x =5;
        return function() {
            return this.x;
        }();
    },
    getY: function() {
        var y =7;
         return this.y;
    }
}
console.log(obj.getX())//3 匿名的自执行函数也指向全局
console.log(obj.getY())//6


/*222222*/
var name="the window";

 var object={
    name:"My Object", 
    getName:function(){ 
       return this.name;
   } 
 }

 object.getName();   //"My Object"

(object.getName)();   //"My Object"

(object.getName=object.getName)();   //"the window",函数赋值会改变内部this的指向,这也是为什么需要在 React 类组件中为事件处理程序绑定this的原因;


/*333333*/
var a=10; 
 var obt={ 
   a:20, 
   fn:function(){ 
     var a=30; 
     console.log(this.a)
   } 
 }
 obt.fn();  // 20

 obt.fn.call(); // 10

 (obt.fn)(); // 20

 (obt.fn,obt.fn)(); // 10

 new obt.fn(); // undefined


/*444444*/
function a(xx){
  this.x = xx;
  return this
};
var x = a(5);
var y = a(6);

console.log(x.x)  // undefined
console.log(y.x)  // 6

/*-----------题目一-------------------*/

           var obj = {   
                 a: 10,
                 b: this.a + 10, //这里的this指向window(全局),a为undefined  ==>  undefined + 20 = NaN
                 fn: function () {
                     return this.a;
                   }
             }
            console.log(obj.b);        //NaN
            console.log(
                obj.fn(),         //20
                obj.fn            //fn
            );     



/**-------------题目二 ----------------*/
            var a = 20; 
            var obj = {
                a: 10,
                getA: function () {
                    return this.a;
                  }
            }
            console.log(obj.getA());    //10
            var test = obj.getA;
            console.log(test());        //20   独立调用test


/*-----------题目三-------------------*/ 

             var a = 5;
             function fn1(){
                 var a = 6;
                 console.log(a);        //6
                 console.log(this.a);   //5
             }  
             function fn2(fn) {
                 var a = 7;
                 fn();
            } 
             var obj = {
                 a: 8,
                 getA: fn1
             }  
            fn2(obj.getA); 


/*-----------题目四-------------------*/ 
               function fn( ) {
               'use strict';
                var a = 1;
                var obj = {
                    a: 10,
                    c: this.a + 20        //严格模式下,a指向undefined嘛,undefined.a报错
                }
                return obj.c;
              }
             console.log(fn());       //输出报错==》 a undefined


/*-----------题目五-------------------*/ 
            // 声明一个构造函数 
             function Person(name, age) {
                this.name = name;
                this.age = age;
                console.log(this);      //与下面的this是一样的,都是Person
            }   
            // Person();           //this 指向window
            Person.prototype.getName = function () {
                console.log(this);      //与上面的this是一样,都是Person
            }; 
            var p1 = new Person("test", 18);
            p1.getName();


/*--------- 题目六 -----*/ 
             var obj = {
               foo: "test",
               fn: function(){
                   var mine = this;
                   console.log(this.foo);       //test
                   console.log(mine.foo);       //test
                   (function(){
                      console.log(this.foo);    //undefined
                      console.log(mine.foo);    //test
                   })();  
               } 
            };
            obj.fn();
              /**
                (1) 在外部函数中, this 和 mine两者都指向了obj,因此两者都可以正确地引用访问foo;
               (2) 在内部函数中,  this不再指向obj, this.foo 没有在内部函数中被定义,
                 而指向到本地的变量mine保持在范围内,并且可以访问(在ES5之前,内部函数的this将指向全局的
                 window对象; 则作为ES5,内部函数中的this是未定义的。)
            */



/*  --------- 题目七 -----------  */ 
           function foo(){
                console.log(this.a);
            }
            var a = 2;
            var o = {
                a:3, 
                foo: foo
            };
            var p = { a:4 };
            o.foo();  //3
            (p.foo = o.foo)();      //2
            /**
                相当于: 
                function w(){
                    p.foo = o.foo;
                }
                w();
                此时的constructor指向window,调用这个w,这个w是在window下创建的,相当于
                调用window.w(),所以constructor指向window。
            */
            p.foo = o.foo;
            p.foo();    //4     函数由p执行,那么constructor指向的就是对象p,谁调用就指向谁0.0
            //this也就指向p, 因此this.a === p.a


 /*  --------- 题目八 -----------  */ 

 //明确绑定的优先权要高于 隐含绑定
            function foo() {
                console.log(this.a);
            }
            var obj1 = {
                a: 3,
                foo: foo
            };    
            var obj2 = {
                a: 5,
                foo: foo
            };
            obj1.foo();     //3
            obj2.foo();     //5
            obj1.foo.call(obj2);    //5
            obj2.foo.call(obj1);    //3
            // new 绑定的优先级高于隐含绑定,并且new 和call/ apply不能同时使用,所以
            // new foo.call(obj1)是不允许的,也就是不能直接对比测试 new绑定 和 明确绑定


/*  --------- 题目九 有意思的一题-----------  */ 
          function test(arg) {
                this.x = arg;
                return this;
            } 
            /**
               var x = test(5);  -->  window.x = window.test(5);
            */
            var x = test(5);     //此时 x = window, y = undefined
            var y = test(6);     //此时 x = 6,  y = window , 后面申请的x会覆盖掉第一次在this.x 生成的window.x
            console.log(x.x);     //undefined,   实际上是6.x  是undefined
            console.log(y.x);     //6     实际上是window.x 也就是6


/*  --------- 题目十 -----------  */ 
              var obj = {
                data: [1,2,3,4,5],
                data2: [1,2,3,4,5],
                fn: function () {
                   console.log("--test--");
                   console.log(this);   //{data: Array(5), data2: Array(5), fn: ƒ, fn2: ƒ}
                   return this.data.map(function (item) {
                         console.log(this);     //  -->  window
                         return item * 2;
                    }); 
                },
                fn2: function () {
                   console.log("---test2---");
                   console.log(this);   //{data: Array(5), data2: Array(5), fn: ƒ, fn2: ƒ}
                   return this.data2.map(item=>{
                       console.log(this);   //  --> obj {data: Array(5), data2: Array(5), fn: ƒ, fn2: ƒ}
                       return item * 2; 
                   });
                }
             };  
             obj.fn()
             obj.fn2();

JS闭包问题

定义:
闭包是指有权访问另外一个函数作用域中的变量的函数, 能够读取其他函数内部变量的函数。
闭包的作用:
正常函数执行完毕后,里面声明的变量被垃圾回收处理掉,但是闭包可以让作用域里的 变量,在函数执行完之后依旧保持没有被垃圾回收处理掉

  1. 闭包实现generator函数
function generator(input) {
    var index = 0;
    return {
      next: function() {
        if (index < input.length) {
          index += 1;
          return input[index - 1];
        }
        return "";
      } 
    }
  }

var mygenerator = generator("boomerang");
mygenerator.next(); // returns "b"
mygenerator.next() // returns "o"
mygenerator = generator("toon");
mygenerator.next(); // returns "t"
  1. 闭包实现计数器
function addCount() {
  var conut = 0;
  return function() {
    count = count + 1;
    console.log(count);
  };
}
var fun1 = addCount();
fun1() // 1
fun1() // 2
var fun2 = addCount();
fun2() // 1
fun2() // 2
  1. 闭包用于封装
function person(name) {
    // 变量作用域为函数内部,外部无法访问,防止了变量名冲突和污染
    var name = '小明';
    this.sayName= function() {
        alert(name)
    }
    this.changeName= function(newName) {
        name = newName
    }
}
// 外部无法访问内部变量
let a = new person()
console.log(a.name) // undefiend
a.changeName('小白')
// 这里修改的name会保存下来
a.sayName() // 小白
  1. js自执行函数
(function(){
    
}());

// 相当于
var b=function () {
   
    }
b()

//给自执行函数命名
function b(){
    ...
}()

//给自执行函数传参
function b(i){
    console.log(i)
}(5)

应用

/*for 循环 + setTimeout*/
for (var i = 0; i < 4; i++) {
  setTimeout(function() {
    console.log(i);
  }, 300);
}

//方法一:
for (var i = 0; i < 4; i++) {
  setTimeout(
    (function(i) {
      return function() {
        console.log(i);
      };
    })(i),
    300
  );
}
// 或者
for (var i = 0; i < 4; i++) {
  setTimeout(
    (function() {
      var temp = i;
      return function() {
        console.log(temp);
      };
    })(),
    300
  );
}
//这个是通过自执行函数返回一个函数,然后在调用返回的函数去获取自执行函数内部的变量,此为闭包

//方法二:
for (var i = 0; i < 4; i++) {
  (function(i) {
    setTimeout(function() {
      console.log(i);
    }, 300);
  })(i);
}
// 方法二是通过创建一个自执行函数,使变量存在这个自执行函数的作用域里

JS正则表达式

基础:
https://www.jianshu.com/p/488d60349325
正则表达式表单验证实例:

/*是否带有小数*/
// `\d`匹配所有数字,`\.`对小数点转义,`^` `$`保证以数字开头以数字结尾
function  isDecimal(strValue )  {  
   var  objRegExp= /^\d+\.\d+$/;
   return  objRegExp.test(strValue);  
}  

/*校验是否中文名称组成 */
function ischina(str) {
    var reg=/^[\u4E00-\u9FA5]{2,4}$/;   /*定义验证表达式*/
    return reg.test(str);     /*进行验证*/
}

/*校验是否全由8位数字组成 */
function isStudentNo(str) {
    var reg=/^[0-9]{8}$/;   /*定义验证表达式*/
    return reg.test(str);     /*进行验证*/
}

/*校验电话码格式 */
function isTelCode(str) {
    var reg= /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/;
    return reg.test(str);
}

/*校验邮件地址是否合法 */
function IsEmail(str) {
    var reg=/^\w+@[a-zA-Z0-9]{2,10}(?:\.[a-z]{2,4}){1,3}$/;
    return reg.test(str);
}

replace的高级用法

// 将除了第一个小a之外所有a大写
'adobe aacc bbaa'.replace(/a+/g, function(str, index, source){
    if(index > 0){
        return str.toUpperCase();
    } else {
        return str;
    }
});
// "adobe AAcc bbAA"
//规则:以1开头第二位为3、5、7、8且长度为11位的数字组合
reg = /^1[3578]\d{9}$/;
var res = reg.test(13600001111); // true

//规则:提取字符串中的数字
'(12.3 - 32.3)*2 = -40'.match(/-?\d+(\.\d+)?/g);
// ["12.3", "32.3", "2", "-40"]

map, reduce, filter, forEach

  1. map和forEach区别
    map和forEach都对数组进行操作
var arr = ['a','b','c','d'];

arr.forEach(function(item,index,arr){    //item表示数组中的每一项,index标识当前项的下标,arr表示当前数组
    console.log(item);
    console.log(index);
    console.log(arr);
    console.log(this);
},123);      //这里的123参数,表示函数中的this指向,可写可不写,如果不写,则this指向window


arr.map(function(item,index,arr){   //参数含义同forEach
    console.log(item);
    console.log(index);
    console.log(arr);
    console.log(this);
},123);

map返回新的数组,forEach不反悔任何值

var a = arr.forEach(function(item,index,arr){ 
    return 123
});
var b = arr.map(function(item,index,arr){
    return 123
}); 
console.log(a);    //undefined
console.log(b);    //[123,123,123,123]

(2) map用于更新数组

var b = arr.map(function(item,index,arr){
    return item+'a';
}); 

console.log(b); //["aa", "ba", "ca", "da"]

(3) forEach代替for循环

//普通的for循环  
for (var index = 0; index < myArray.length; index++) {  
  console.log(myArray[index]);  
}  

//从ES5开始提供这样的for循环  
myArray.forEach(function (value) {  
  console.log(value);  
});  

// 在ES6我们还可以这样任性  
// 循环下标或者key(for-in)  
for (var index in myArray) {    
  console.log(myArray[index]);  
}  
  
// 循环value(for-of)  
for (var value of myArray) {  
  console.log(value);  
}  
  
// 甚至直接循环key和value
for (var [key, value] of phoneBookMap) {  
  console.log(key + "'s phone number is: " + value);  
}  
  
// 或者更者我们这样“优雅”的循环对象(貌似和ES6没有关系)  
for (var key of Object.keys(someObject)) {  
  console.log(key + ": " + someObject[key]);  
}  

JS字符串类面试题

  1. 解析URL的Parameters
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果
{ user: 'anonymous',
  id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型
  city: '北京', // 中文需解码
  enabled: true, // 未指定值得 key 约定为 true
}
*/

function parseParam(url) {
  //`.+?.+$` 对带问号的url进行简单匹配  `(.+)`将问号后面的部分分组取出来
  const paramsStr = /.+\?(.+)$/.exec(url)[1]; 
  const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
  let paramsObj = {};
  // 将 params 存到对象中
  paramsArr.forEach(param => {
    if (/=/.test(param)) { // 处理有 value 的参数
      let [key, val] = param.split('='); // 分割 key 和 value
      val = decodeURIComponent(val); // 解码
      val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字

      if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
        paramsObj[key] = [].concat(paramsObj[key], val);
      } else { // 如果对象没有这个 key,创建 key 并设置值
        paramsObj[key] = val;
      }
    } else { // 处理没有 value 的参数
      paramsObj[param] = true;
    }
  })

  return paramsObj;
}
  1. render方法
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年龄18,性别undefined
function render(template, data) {
  const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
  if (reg.test(template)) { // 判断模板里是否有模板字符串
    const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
    template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
    return render(template, data); // 递归的渲染并返回渲染后的结构
  }
  return template; // 如果模板没有模板字符串直接返回
}
  1. 驼峰命名
var s1 = "get-element-by-id"

// 转化为 getElementById
var f = function(str){
  var reg = /-\w/g
  return str.replace(reg, function(item, index, source){
    return item.slice(1).toUpperCase();
  }) 
}
  1. 实现千位分隔符
// 保留三位小数
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.321); // return '1,087,654.321'
function parseToMoney(num) {
  num = parseFloat(num.toFixed(3));
  let [integer, decimal] = String.prototype.split.call(num, '.');
  integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
  return integer + '.' + (decimal ? decimal : '');
}

  1. 字符串查找
a='34';b='1234567'; // 返回 2
a='35';b='1234567'; // 返回 -1
a='355';b='12354355'; // 返回 5
isContain(a,b);
function isContain(a, b){
  for(let i in b){
    if(b[i] === a[0]){
      let temp = true;
      for(let j in a){
        if(a[j] !== b[i + j]){
          temp = false;
        }
      }
    if(temp) return i;
    }
  }
 return false;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353