CruxF / Blog

个人博客,记载学习的点点滴滴,怕什么技术无穷,进一寸有一寸的欢喜:sparkles:
63 stars 19 forks source link

JavaScript进阶学习总结与资料 #10

Open CruxF opened 6 years ago

CruxF commented 6 years ago

前言

该份资料的来源为慕课网教程《JavaScript深入浅出》,内容几乎是全文摘抄下来,不喜勿喷啊。

数据类型

JavaScript被称为是一种弱类型的语言,原因就是数据类型居然能够随意转换而不报错,而且在定义变量的时候不用指定其类型,示例代码如下:

var num = 32;
num = "this is a string";

面试题常问的:JavaScript中原始类型有哪几种? 答:number、string、boolean、null、undefined。

隐式转换

1、加号(+)和减号(-) 在数字与字符串做运算的时候,加号做拼接,减号就做减法

"37" - 7   //30
"37"+7   //377

2、等于号(==) 等于号判断原始类型的时候会有自动类型转换的特点,在判断引用类型的时候会从引用地址上进行判断。

"1.23" == 1.23   //true
0 == false   //true
null == undefined   //true
new Object() == new Object()   //false
[1, 2] == [1, 2]   //false
new String('hi') == 'hi'   //true
new String('456') == 456    //true

3、严格等于(===) 相比于等于号,严格等于不会进行类型转换,它会一开始判断两者之间的类型,如果类型不同,直接返回false。类型相同且内容相同才返回true。

"1.23" === 1.23   //false
0 === false   //false
null === undefined   //false
new Object() === new Object()   //false
[1, 2] === [1, 2]   //false
new String('hi') === 'hi'   //false
new String('456') === 456    //false

包装对象

JavaScript是面向对象的语言,使用”.”操作符可以访问对象的属性和方法,而对于基本类型(null,undefined, bool, number, string)应该是值类型,没有属性和方法,然而

var str = "this is a string";
console.log(str.length); //16
console.log(str.indexOf("is")); //2

结果很简单,但是仔细想想还真奇怪,string不是值类型吗!怎么又有属性又有方法的!其实只要是引用了字符串的属性和方法,JavaScript就会将字符串值通过new String(s)的方式转为内置对象String,一旦引用结束,这个对象就会销毁。所以上面代码在使用的实际上是String对象的length属性和indexOf方法。

同样的道理,数字和布尔值的处理也类似。null和undefined没有对应对象。既然有对象生成,能不能这样

var str = "this is a string";
str.b = 10;
console.log(str.b);  //undefined

结果并没有返回10,而是undefined!不是说好了是个对象吗!正如刚才提到第二行代码只是创建了一个临时的String对象,随即销毁,第三行代码又会创建一个新的临时对象(这就是低版本IE频繁处理字符串效率低的一个原因),自然没有b属性,这个创建的临时对象就成为包装对象。

类型检测

在JavaScript中,有很多种检测数据的类型,主要是有以下几种

1、typeof:一般用于检测原始数据类型,引用数据类型无法具体的检测出来

console.log(typeof ""); //string
console.log(typeof 1); //number
console.log(typeof true); //boolean
console.log(typeof null); //object
console.log(typeof undefined); //undefined
console.log(typeof []); //object
console.log(typeof function() {}); //function
console.log(typeof {}); //object

其实null是js设计的一个败笔,早期准备更改null的类型为null,由于当时已经有大量网站使用了null,如果更改,将导致很多网站的逻辑出现漏洞问题,就没有更改过来,于是一直遗留到现在。

2、instanceof:检测引用数据类型

console.log("1" instanceof String);  //false
console.log(1 instanceof Number);  //false
console.log(true instanceof Boolean);  //false
console.log([] instanceof Array);  //true
console.log(function() {} instanceof Function);  //true
console.log({} instanceof Object);  //true

可以看到前三个都是以对象字面量创建的基本数据类型,但是却不是所属类的实例,这个就有点怪了。后面三个是引用数据类型,可以得到正确的结果。如果我们通过new关键字去创建基本数据类型,你会发现,这时就会输出true,如下:

console.log(new String("1") instanceof String); //true
console.log(new Number(1) instanceof Number); //true
console.log(new Boolean(true) instanceof Boolean); //true
console.log([] instanceof Array); //true
console.log(function() {} instanceof Function); //true
console.log({} instanceof Object); //true

3、constructor:似乎完全可以应对基本数据类型和引用数据类型,都能检测出数据类型

console.log(("1").constructor === String); //true
console.log((1).constructor === Number); //true
console.log((true).constructor === Boolean); //true
console.log(([]).constructor === Array); //true
console.log((function() {}).constructor === Function); //true
console.log(({}).constructor === Object); //true

事实上并不是如此,来看看为什么:

function Fn(){};
Fn.prototype=new Array();
var f=new Fn();

console.log(f.constructor===Fn);  //false
console.log(f.constructor===Array);  //true

声明了一个构造函数,并且把他的原型指向了Array的原型,所以这种情况下,constructor也显得力不从心了。

4、Object.prototype.toString:终极数据检测方式

var a = Object.prototype.toString;

console.log(a.call("aaa"));  //[object String]
console.log(a.call(1));  //[object Number]
console.log(a.call(true));  //[object Boolean]
console.log(a.call(null));  //[object Null]
console.log(a.call(undefined));  //[object Undefined]
console.log(a.call([]));  //[object Array]
console.log(a.call(function() {}));  //[object Function]
console.log(a.call({}));  //[object Object]

表达式和运算符

表达式

概念 表达式是指能计算出值的任何可用程序单元,或者可以这么说:表达式是一种JavaScript短语,可以使JavaScript解释器用来产生一个值。

原始表达式

复合表达式 比如:10*20

数组和对象的初始化表达式 比如:[1,2]、{x:1,y:2}等这些。

函数表达式 比如:var fe = function(){}或者(functiong(){console.log('hello world');})()

属性访问表达式 比如:var o = {x:1},访问属性的方式有o.x或者o['x']

调用表达式 比如:funName()

对象创建表达式 比如:new Func(1,2)或者new Object

运算符

太多太基础啦,不多说,稍微提一下以下几个运算符

// 逗号运算符
var n = (1,2,3)  //n=3

// 删除运算符
var obj = {x:1}
obj.x;  //1
delete obj.x;
obj.x;  //undefined

var obj = {};
Object.defineProperty(obj,'x',{
  configurable:false,
  value:1
});
delete obj.x;  //false
obj.x;  //1

// in运算符
window.x = 1;
'x'  in window;  //true

// new运算符
function Foo(){}
Foo.prototype.x = 1;
var obj = new Foo();
obj.x;  // 1
obj.hasOwnProperty('x');  // false
obj.__proto__.hasOwnProperty('x');  // true

// this运算符
this;
var obj = {
    func:function(){return this;}
};
obj.func();  //obj

语句

block语句和var语句

block语句 块语句常用于组合0~N个语句,往往用一对花括号定义。

{
  var str = 'hi';
  console.log(str);
}

if(true) {
  console.log('hi');
}

在ES6出来之前,JavaScript是没有块级作用域的,具体看以下两段代码:

for (var i=0; i<10; i++) {                
  var str = 'hi';  
  console.log(str); 
}  
等同于
 var i = 0;
for (; i<10; i++) {                
  var str = 'hi';  
  console.log(str); 
} 

{
  var x = 1;
}
等同于
var x = 1;
{

}

function foo() {
  var a = 1;
  console.log(a);  //1
}
foo();
console.log(typeof a);  //undefined

声明语句var

function foo() {
  //隐式的将b定义为全局变量
  var a = b = 1;
}
foo();
console.log(typeof a);  //undefined
console.log(typeof b);  //number

function foo() {
  //同时定义多个变量的正确方式
  var a =1,b = 1;
}
foo();
console.log(typeof a);  //undefined
console.log(typeof b);  //undefined

try-catch语句

try跟catch搭配使用可以检测try里边的代码有没有抛出error,如果有error就会跳转到catch里执行catch里的程序。执行顺序为:先try捕获异常,执行catch里面的内容,最后执行finally里面的内容,当然也可以只写catch或者finally两个中的一个,但是必须写try语句块。

try {
  throw 'test';
} catch(e) {
  console.log(e); //test
} finally {
  console.log('必須執行的');
}

嵌套使用

try {
  try {
    throw new Error('oops');
  } finally {
    console.log('finally');
  }
} catch(e) {
  console.error('outer', e.message);
}

由于在嵌套层中并没有catch语句,因此输出结果的顺序为:finally、outer、oops。下面请看含有catch语句的

try {
  try {
    throw new Error('oops');
  } catch(ex) {
    console.log('inner', ex.message);
  } finally {
    console.log('finally');
  }
} catch(ex) {
  console.error('outer', ex.message);
}

以上代码输出结果的顺序为:inner、oops、finally。原因是异常已经在内部处理过了,因此不会再到外部去处理。

更复杂的嵌套

try {
  try {
    throw new Error('oops');
  } catch(ex) {
    console.log('inner', ex.message);
    throw ex;
  } finally {
    console.log('finally');
  }
} catch(ex) {
  console.error('outer', ex.message);
}

以上代码输出结果的顺序为:inner、oops、finally、outer、oops。原因在内部的catch语句重新向外抛出了ex这个异常。

for in语句

有以下的特点:1、顺序不确定;2、enumerable为false时不会出现;3、fon in对象属性时会受到原型链的影响

var p;
var obj = { x: 1, y: 2 }
for(p in obj) {
  console.log(p);  //x  y
}

严格模式

这里有一篇非常好的总结,传送门

对象

概述

对象中包含一系列的属性,这些属性是无序的,每一个属性都有一个字符串key和对应的value

var obj = {x:1,y:2};
obj.x;  //1
obj.y; //2

创建对象

创建对象方式1—字面量:

var obj1 = {x:1,y:2};
var obj2 = {
  x:1,
  y:2,
  o:{
    z:3,
    n:4
  }
};

创建对象方式2—通过构造函数

function foo() {}
foo.prototype.z = 3;
var obj = new foo();
obj.x = 1;
obj.y = 2;
console.log(obj.x); //1
console.log(obj.y); //2
console.log(obj.z); //3
console.log(typeof obj.toString()); //string
console.log(typeof obj.toString);  //function
console.log('z' in obj); //true
console.log(obj.hasOwnProperty('z')); //false

创建对象方式3—Object.create

var obj = Object.create({ x: 1 });
console.log(obj.x);  //1
console.log(obj.toString);  //function
console.log(obj.hasOwnProperty('x'));  //false

// null对象的原型链少了Object这一层
var obj1 = Object.create(null);
console.log(obj1.toString);  //undefined

属性操作

属性读写

var obj = {x:1,y:2};
obj.x;  //1
obj['y'];  //2

//使用[]取得属性值一般场景
var obj = {x1:1,x2:2};
for(var i=1; i<=2; i++){
  console.log(obj['x' + i];
}

属性删除

var person = {age:28,title:'test'};
delete person.age;  //true
delete preson['title'];  //true
person.age;  //undefined
delete person.age;  //true

//无法删除原型
delete Object.prototype;  //false
//因为原型的configurable不可配置
var descriptor = Object.getOwnPropertyDescriptor(Object,'prototype');
descriptor.configurable;  //false

属性检测

var cat = new Object();
cat.legs = 4;
cat.name = 'tom';

//for in检测
'legs' in cat;  //true
'abc' in cat;  //false
'toString' in cat;  //true

//检测对象本身的属性
cat.hasOwnProperty('legs');  //true
cat.hasOwnProperty('toString');  //false

//检测对象的属性是否可枚举,包括原型上的
cat.propertyIsEnumerable('legs');  //true
cat.propertyIsEnumerable('toString');  //false

属性枚举

var o = { x: 1, y: 2, z: 3 };
'toString' in o; //true
o.propertyIsEnumerable('toString'); //false
var key;
for(key in o) {
  console.log(key); //x,y,z
}

var obj = Object.create(o);
obj.a = 4;
var key;
for(key in obj) {
  console.log(key); //a,x,y,z
}

var obj = Object.create(o);
obj.a = 4;
var key;
for(key in obj) {
  if(obj.hasOwnProperty(key)) {
    console.log(key); //a
  }
}

getter()和setter()方法

var man = {
  name: 'tom',
  weibo: 'haha',
  get age() {
    return new Date().getFullYear() - 1988;
  },
  set age(val) {
    console.log('你没有权限设置年龄为:' + val);
  }
}
console.log(man.age);  //30
man.age = 100;  //你没有权限设置年龄为:100
console.log(man.age);  //30

//修改一下,使上面的代码更复杂
var man = {
  name: 'tom',
  weibo: 'haha',
  $age: null,
  get age() {
    if(this.$age == undefined) {
      return new Date().getFullYear() - 1988;
    } else {
      return this.$age;
    }
  },
  set age(val) {
    val = +val;
    if(!isNaN(val) && val > 0 && val < 150) {
      this.$age = +val;
    } else {
      console.log('年龄无法设置为:' + val);
    }
  }
}
console.log(man.age); //30
man.age = 100;
console.log(man.age); //100
man.age = 'abc'; //年龄无法设置为:NaN

get/set与原型链

//get和set设置的值不会被直接更改
function foo() {}
Object.defineProperty(foo.prototype, 'z', {
  get: function() {
    return 1;
  }
})
var obj = new foo();
console.log(obj.z); //1
obj.z = 10;
console.log(obj.z); //1

//defineProperty设置的值不会被直接更改
var o = {}
Object.defineProperty(o, 'x', { value: 5 });
var obj = Object.create(o);
console.log(obj.x); //5
obj.x = 200;
console.log(obj.x); //5

//更改defineProperty设置的值
var o = {}
Object.defineProperty(o, 'x', { writable: true, configurable: true, value: 5 });
var obj = Object.create(o);
console.log(obj.x); //5
obj.x = 200;
console.log(obj.x); //200

属性标签

var person = {};
Object.defineProperty(person, 'name', {
  configurable: false,
  writable: false,
  enumerable: true,
  value: 'tom'
})
console.log(person.name);  //tom
person.name = 'jack';
console.log(person.name);  //tom
console.log(delete person.name);  //false
//获得属性的标签值
console.log(Object.getOwnPropertyDescriptor(person,'name'));

//属性标签的应用
var person = {};
Object.defineProperties(person, {
  title: {
    value: 'JavaScript',
    enumerable: true
  },
  corp: {
    value: 'BAT',
    enumerable: true
  },
  salary: {
    value: 50000,
    enumerable: true,
    writable: true
  },
  luck: {
    get: function() {
      return Math.random() > 0.5 ? 'good' : 'bad';
    }
  },
  promote: {
    set: function(level) {
      this.salary *= 1 + level * 0.1;
    }
  }
});
console.log(Object.getOwnPropertyDescriptor(person, 'salary'));
console.log(person.salary); //50000
person.promote = 2;
console.log(person.salary); //60000

image

序列化

//将属性和值序列化为字符串
var obj = { x: 1, y: true, z: [1, 2, 3], nullVal: null };
//{"x":1,"y":true,"z":[1,2,3],"nullVal":null}
console.log(JSON.stringify(obj));

//有点小坑,得注意
var obj1 = { val: undefined, a: NaN, b: Infinity, c: new Date() };
//{"a":null,"b":null,"c":"2018-06-09T10:46:01.929Z"}
console.log(JSON.stringify(obj1));

//把字符串反序列化为属性和值
var obj2 = JSON.parse('{"x":2333}');
//2333
console.log(obj2.x);

数组

数组中的方法

将数组转为字符串

var arr = [1, 2, 3];
console.log(arr.join());  //1,2,3
console.log(arr.join('-'));  //1-2-3

function repeatString(str, n) {
  return new Array(n + 1).join(str);
}
console.log(repeatString('a', 3));  //aaa
console.log(repeatString('hi', 2));  //hihi

将数组逆序输出

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

将数组进行排序

var arr = [13, 24, 52, 3];
//13,24,3,54
console.log(arr.sort());
//13,24,3,54
console.log(arr);
var arrSort = arr.sort(function(a, b) { return a - b; });
//3,13,24,52
console.log(arrSort);

var arr1 = [
  { age: 25 }, 
  { age: 39 }, 
  { age: 22 }
];
arr1.sort(function(a, b) {
  return a.age - b.age;
})
arr1.forEach(function(item) {
  console.log('age:', item.age);
})

将数组合并

var arr = [1, 2, 3];
//[1,2,3,4,5]
console.log(arr.concat(4, 5));
//[1,2,3]
console.log(arr);
//[1,2,3,10,11,13]
console.log(arr.concat([10, 11], 13));
//[1,2,3,1,[2,3]]
console.log(arr.concat([1, [2, 3]]));

返回部分数组

var arr = [1, 2, 3, 4, 5];
//[2,3]
console.log(arr.slice(1, 3));
//[2,3,4,5]
console.log(arr.slice(1));
//[2,3,4]
console.log(arr.slice(1, -1));
//[2]
console.log(arr.slice(-4, -3));
//[1,2,3,4,5]
console.log(arr);

将数组进行拼接

var arr = [1, 2, 3, 4, 5];
//[3,4,5],从下标为2开始删除
console.log(arr.splice(2));
//[1,2]
console.log(arr);

var arr1 = [1, 2, 3, 4, 5];
//[3,4],从下标为2开始删除,删除两个元素后停止
console.log(arr1.splice(2, 2));
//[1,2,5]
console.log(arr1);

var arr2 = [1, 2, 3, 4, 5];
//[2],从下标为1开始删除,删除一个元素后停止,并且添加a和b这两个元素
console.log(arr2.splice(1, 1, 'a', 'b'));
//[1,"a","b",3,4,5]
console.log(arr2);

将数组进行遍历

var arr = ['a', 'b', 'c', 'd', 'e'];
arr.forEach(function(x, index, a) {
  console.log(x + '-' + index + '-' + (a === arr));
});

将数组进行映射

var arr = [1, 2, 3];
arr.map(function(x) {
  //11,12,13
  console.log(x + 10);
});
//[1,2,3]
console.log(arr);

将数组进行过滤

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
arr.filter(function(x, index) {
  //1,2,3,4,5,6,7,8,9,10
  console.log(x);
  //0,1,2,3,4,5,6,7,8,9
  console.log(index);
  return index % 3 === 0 || x >= 8;
});
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(arr);

将数组进行判断

var arr = [1, 2, 3, 4, 5];
//判断每一个元素是否小于10
arr.every(function(x) {
  return x < 10;
});
//判断是否有一个元素等于3
arr.some(function(x) {
  return x === 3;
});

将数组元素进行累加操作

var arr = [1, 2, 3];
var sum = arr.reduce(function(x, y) {
  return x + y;
});
//6
console.log(sum);
//[1,2,3]
console.log(arr);

var arr1 = [3, 9, 6];
var max = arr1.reduce(function(x, y) {
  //3-9
  //9-6
  console.log(x + '-' + y);
  return x > y ? x : y;
});
//9
console.log(max);
//[3,9,6]
console.log(arr1);

var arr2 = [3, 9, 6];
var max2 = arr2.reduceRight(function(x, y) {
  //6-9
  //9-3
  console.log(x + '-' + y);
  return x > y ? x : y;
});
//9
console.log(max2);
//[3,9,6]
console.log(arr2);

将数组进行检索

var arr = ['a', 'b', 'c', 'd', 'e', 'a'];
//2,在数组下标为2的位置找到
console.log(arr.indexOf('c'));
//-1,找不到该元素
console.log(arr.indexOf('f'));
//0,在数组下标为0的位置找到,默认是从左到右查找
console.log(arr.indexOf('a'));
//5,在数组下标为5的位置找到,设置是从数组下标为1的位置开始向右查找
console.log(arr.indexOf('a', 1));
//5,在数组下标为5的位置找到,-3表示在d的位置开始向右查找
console.log(arr.indexOf('a', -3));
//0,在数组下标为0的位置找到,-6表示在最左a的位置开始向右查找
console.log(arr.indexOf('a', -6));
//-1,找不到该元素,-3表示在d的位置开始向右查找
console.log(arr.indexOf('b', -3));
//3,在数组下标为3的位置找到
console.log(arr.lastIndexOf('d'));
//-1,找不到该元素,2表示的是在e的位置向右查找
console.log(arr.lastIndexOf('d', 2));
//3,在数组下标为3的位置找到,5表示的是在b的位置向右查找
console.log(arr.lastIndexOf('d', 5));

判断是否为数组

//true
console.log(Array.isArray([]));
//true
console.log(({}).toString.apply([]) === '[object Array]');
//true
console.log([] instanceof Array);
//true
console.log([].constructor === Array);

数组 VS 对象

数组 VS 字符串

字符串是一种类数组

var str = 'hello world';
str.charAt(0);  //h
str[1];  //e

Array.prototype.join.call(str,'-');  //h-e-l-l-o--w-o-r-l-d

函数和作用域

函数概念

函数是一块JavaScript代码,被定义一次,但可以执行和调用多次。JavaScript中的函数也是对象,所以函数可以像其他对象那样操作和传递,所以我们也常叫JavaScript中的函数为函数对象。

函数调用方式

函数声音和函数表达式

函数声明

function add(a, b) {
  var x = a;
  var y = b;
  return x + b;
}

函数表达式

//一般表达式
var add = function(a, b) {

}
//立即调用表达式
(function() {

})();
//返回表达式
return function() {

};
//命名函数表达式
var add = function foo(a, b) {

}

函数声明和函数表达式的区别 他们最重要的区别就是函数声明能够提前,而函数表达式不行

var num = add(1, 3);
//4
console.log(num);
function add(a, b) {
  var x = a;
  var y = b;
  return x + b;
}

var num1 = add1(1, 3);
//add1 is not a function
console.log(num1);
var add1 = function(a, b) {
  var x = a;
  var y = b;
  return x + b;
}

this的栗子

全局的this(浏览器)

console.log(this.document == document);  //true
console.log(this === window);  //true

this.a = 37;
console.log(window.a);  //37

一般函数中的this

function f1(){
  return this;
}
f1() === window;  //true

function f2(){
'use strict';
return this;
}
f2() === undefined;  //true

作为对象方法中函数的this

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};
console.log(o.f()); //37

var o = { prop: 37 };
function independent() {
  console.log(this.prop); //37
  return this.prop;
}
o.f = independent;
console.log(o.f()); //37

对象原型链上的this

var o = {
  fn: function() {
    return this.a + this.b;
  }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
//5
console.log(p.fn());

get/set方法与this

function modulus() {
  //abs()返回数的绝对值
  return Math.abs(this.re + this.im);
}
var o = {
  re: 4,
  im: -9,
  get phase() {
    //sqrt()返回数的平方根
    return Math.sqrt(this.re);
  }
};
Object.defineProperty(o, 'modulus', {
  get: modulus,
  enumerable: true,
  configurable: true
});
console.log(o.phase); //2
console.log(o.modulus); //5

构造器中的this

function Abc() {
  this.a = 33;
}
var oo = new Abc();
console.log(oo.a); //33

function Bcd() {
  this.b = 55;
  return { b: 66 };
}
var pp = new Bcd();
console.log(pp.b); //66

call/apply方法与this

function add(c, d) {
  return this.a + this.b + c + d;
}
var o = { a: 1, b: 3 };
//16
console.log(add.call(o, 5, 7));
//34
console.log(add.apply(o, [10, 20]));

function bar() {
  //[object Window]
  console.log(Object.prototype.toString.call(this));
}
bar();
//[object Number]
console.log(bar.call(10));

bind方法与this

function bar() {
  return this.a;
}
var ger = bar.bind({
  a: 'test'
});
//f bar(){ return this.a; }
console.log(ger);
//test
console.log(ger());

var odr = {
  a: 33,
  bn: bar,
  gn: ger
};
//33
console.log(odr.bn());
//test
console.log(odr.gn());

函数属性arguments

function foo(x, y, z) {
  //2
  console.log(arguments.length);
  //3
  console.log(arguments[0]);
  arguments[0] = 100;
  //100
  console.log(x)
  arguments[2] = 200;
  //undefined
  console.log(z);
  //200
  console.log(arguments[2]);
  //true
  console.log(arguments.callee === foo);
}
foo(3, 4);
//3
console.log(foo.length);
//foo
console.log(foo.name);

apply/call方法(浏览器)

function foo(x, y) {
  console.log(x, y, this);
}
//1,2,Number(100)
foo.call(100, 1, 2);
//3,4,Boolean(true)
foo.apply(true, [3, 4]);
//undefined,undefined,window
foo.apply(null);
//undefined,undefined,window
foo.apply(undefined);

function foo(x, y) {
  'use strict';
  console.log(x, y, this);
}
//undefined,undefined,null
foo.apply(null);
//undefined,undefined,undefined
foo.apply(undefined);

bind方法

this.x = 9;
var module = {
  x: 33,
  getX: function() {
    return this.x;
  }
};
console.log(module.getX()); //33

var gn = module.getX;
console.log(gn()); //9

var gn1 = gn.bind(module);
console.log(gn1()); //33

bind与currying 函数有柯里化(currying)的特性,意思函数能拆分成多个单元。

function add(a, b, c) {
  return a + b + c;
}
var fn = add.bind(undefined, 100);
console.log(fn(1, 2)); //103

var fn1 = fn.bind(undefined, 200);
console.log(fn1(10)); //310

柯里化的实际运用

function getConfig(colors, size, otherOptions) {
  console.log(colors, size, otherOptions);
}
var defaultConfig = getConfig.bind(null, '#cc0000', '1024*768');
//#cc0000 1024*768 123
console.log(defaultConfig('123'));
//#cc0000 1024*768 456
console.log(defaultConfig('456'));

bind与new

function foo() {
  this.b = 100;
  return this.a;
}
var fn = foo.bind({ a: 1 });
//1
console.log(fn());
//{b:100}
console.log(new fn());

bind方法模拟

if(!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if(typeof this !== 'function') {
      throw new TypeError('what is trying to be bound is not callable');
    }
    var aArgs = Array.prototype.slice.call(arguments, 1);
    var fToBind = this;
    var fNOP = function() {};
    var fBound = function() {
      return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
    };
    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    return fBound;
  };
}

以上代码有做了些修改,原始代码以及分析请看下图: image

理解闭包

闭包的栗子

function outer() {
  var localVal = 30;
  return localVal;
}
console.log(outer());

function outer1() {
  var localVal2 = 33;
  return function() {
    return localVal2;
  }
}
var fn = outer1();
console.log(fn())

闭包概念 在计算机科学中,闭包(也成为词法闭包或者函数闭包)是指一个函数或者函数的引用,与一个引用环境绑定在一起,这个引用环境是一个存储该函数每个非局部变量(也叫自由变量)的表。

闭包,不同于一般的函数,它允许一个函数在立即词法作用域外调用时,仍可访问非本地变量。

闭包特性

作用域

全局作用域、函数作用域和eval

//全局变量
var a = 10;
(function() {
  //局部变量
  var b = 20;
})();
//10
console.log(a);
//undefined
console.log(b);

for(var item in { a: 1, b: 2 }) {
  //a  b
  console.log(item);
}
//b
console.log(item);

eval('var c = 100');
//100
console.log(c);

作用域链

function outer1() {
  var local1 = 11;
  function inter() {
    var local2 = 22;
    //11
    console.log(local1);
    //22
    console.log(local2);
    //33
    console.log(global1);
  }
  inter();
}
var global1 = 33;
outer1();

function outer2() {
  var local3 = 44;
  var fn = new Function('console.log(typeof local3);');
  //undefined
  fn();
}
outer2();

执行上下文

这个玩意太抽象了,看完视频对这个概念还是十分模糊,还是来这看一下比较容易理解的文章——王福朋深入理解JavaScript系列

OOP知识点

概念与继承

面向对象程序设计是一种程序设计规范,同时也是一种程序开发的方法。对象指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性。

看了课程的相关知识点,发现远远没有下面几篇博文说的这么简单易懂与透彻,在这就不整理课程的内容了,一起来看看以下三个博客中内容: 冴羽博客JavaScript系列 汤姆大叔深入理解JavaScript系列 王福朋深入理解JavaScript系列

JavaScript正则表达式

这节课的内容也不摘抄了,因为之前做过这方面的总结,再继续总结一次我就认为是得不偿失了。 JavaScript正则表达式

参考资料

1、博客园小火柴的前端学习之路 2、冴羽博客JavaScript系列 3、汤姆大叔深入理解JavaScript系列 4、JavaScript包装对象 5、JavaScript检测数据类型四种方法 6、JavaScript中的new到底做了些什么 7、王福朋深入理解JavaScript系列 8、JavaScript正则表达式

Xuwen666 commented 6 years ago

大佬,能留个QQ吗,本人萌新一名想向您请教一些问题。如有打扰到您十分抱歉

CruxF commented 6 years ago

@Xuwen666 我。。。我不是大佬啊,有事的话在这说,会有一定几率遇到真.大佬帮你解决问题

Xuwen666 commented 6 years ago

@CruxF 就是能传授一下学习路径吗,我html,css,js都有一点基础,接下来改如何系统的学习

CruxF commented 6 years ago

@Xuwen666 去知乎寻找答案吧,我也是个正在努力的菜鸟