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的值)。
- this默认指向全局作用域
this === window; // true
function test(){
this.x = 1
console.log(this.x)
}
test() // output : 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
}
- 作为构造函数调用
function Test(){
this.x = 1
}
var o = new test()
console.log(o.x) // output: 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
- 使用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
- 例题
/*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闭包问题
定义:
闭包是指有权访问另外一个函数作用域中的变量的函数, 能够读取其他函数内部变量的函数。
闭包的作用:
正常函数执行完毕后,里面声明的变量被垃圾回收处理掉,但是闭包可以让作用域里的 变量,在函数执行完之后依旧保持没有被垃圾回收处理掉
- 闭包实现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"
- 闭包实现计数器
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
- 闭包用于封装
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() // 小白
- 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
- 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字符串类面试题
- 解析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;
}
- 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; // 如果模板没有模板字符串直接返回
}
- 驼峰命名
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();
})
}
- 实现千位分隔符
// 保留三位小数
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 : '');
}
- 字符串查找
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;
}