JavaScript 精粹 重拾js深入简出

JavaScript 精粹

数据类型

javascript是弱类型语言,是一种轻量级的编程语言。不要小看它。

字符串(String)

数字(Number)

布尔(Boolean)

空(Null)

未定义(Undefined)

对象(Object):数组(Array),正则表达式(RegExp),日期(Date),数学(Math)......

可以使用 typeof 判断数据类型,操作符返回一个字符串,但并非返回的所有结果都符合预期

typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型。

var a;                  //输出值为undefined,数据类型为undefined
var b=2;                //输出值为2,数据类型为number
var c='你好';            //输出值为你好,数据类型为string
var d=[1,2,3];          //输出值为1,2,3,数据类型为object
var e=new Object();     //输出值为[object Object],数据类型为object
    e.aa=1;
    e.bb=2;
var f=true;             //输出值为true,数据类型为boolean
var g=null;             //输出值为null,数据类型为object
var h = new Date();     //输出值为Wed Oct 12 2016 22:42:13 GMT+0800 (中国标准时间),数据类型为object
var i = function(a,c){
            alert(a*c)  //数据类型为function
        };

//最常见的判断方法:typeof

console.log('输出值为'+a+',数据类型为'+typeof a);
console.log('输出值为'+b+',数据类型为'+typeof b);
console.log('输出值为'+c+',数据类型为'+typeof c);
console.log('输出值为'+d+',数据类型为'+typeof d);
console.log('输出值为'+e+',数据类型为'+typeof e);
console.log('输出值为'+f+',数据类型为'+typeof f);
console.log('输出值为'+g+',数据类型为'+typeof g);
console.log('输出值为'+h+',数据类型为'+typeof h);
console.log('输出值为'+i+',数据类型为'+typeof i);


instanceof 运算符简介

在 JavaScript 中,判断一个变量的类型尝尝会用 typeof 运算符,在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 "object"。ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。

var Stringobj = new String("hello world"); 
console.log(Stringobj instanceof String); 	 // 输出 "true"

这段代码问的是“变量 oStringObject 是否为 String 对象的实例?”oStringObject 的确是 String 对象的实例,因此结果是"true"。尽管不像 typeof 方法那样灵活,但是在 typeof 方法返回 "object" 的情况下,instanceof 方法还是很有用的。


使用 instanceof 就是判断一个实例是否属于某种类型

// 判断 foo 是否是 Foo 类的实例
function Foo(){} 
var foo = new Foo(); 
console.log(foo instanceof Foo)//true

instanceof 可以在继承关系中用来判断一个实例是否属于它的父类型。

// 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
function Aoo(){} 
function Foo(){} 
Foo.prototype = new Aoo();//JavaScript 原型继承

var foo = new Foo(); 
console.log(foo instanceof Foo)//true 
console.log(foo instanceof Aoo)//true

根据对象的constructor判断对象类型

var a=[1,2,3];
var b=new Date();
var c=function(){};
console.log(a.constructor === Array);        //true
console.log(b.constructor === Date)          //true
console.log(c.constructor === Function)      //true

constructor是什么,简单的理解,constructor指的就是对象的构造函数。

function Foo(){};
var foo = new Foo();
console.log(foo.constructor);//Foo
console.log(Foo.constructor);//Function
console.log(Object.constructor);//Function
console.log(Function.constructor);//Function 

知道了constructor是什么,不得不讲讲Prototype与Constructor的关系;

在 JavaScript 中,每个函数都有名为“prototype”的属性,用于引用原型对象。此原型对象又有名为“constructor”的属性,它反过来引用函数本身。这是一种循环引用;

function Dog(){}
alert(Dog === Dog.prototype.constructor);//true

注意: constructor 在类继承时会出错

function A(){};
function B(){};
A.prototype=new B(); //A继承自B
var cuu=new A();
console.log(A.constructor===Function);      //true
console.log(cuu.constructor===B);           //true

cuu.constructor=A;                          //将自己的类赋值给对象的constructor属性
console.log(cuu.constructor===A);           //true
console.log(cuu.constructor===B);           //false

使用Object.prototype上的原生toString()方法判断数据类型

var aa='String';
var bb=123;
var cc=[1,2,3];
var dd=new Date();
var ee=function(){};
console.log(Object.prototype.toString.call(aa) === '[object String]');//true
console.log(Object.prototype.toString.call(bb) === '[object Number]');//true
console.log(Object.prototype.toString.call(cc) === '[object Array]');//true
console.log(Object.prototype.toString.call(dd) === '[object Date]'); //true
console.log(Object.prototype.toString.call(ee) === '[object Function]');//true
console.log(ee.toString.call()); //toString()函数用于将当前对象以字符串的形式返回。

变量

在应用程序中,使用变量来来为值命名。变量的名称称为 identifiers

声明

1.使用关键字 var :函数作用域

2.使用关键字 let :块作用域 (block scope local variable)

3.直接使用:全局作用域

var obj = 1;
console.log(obj);//1
(function() {
  var varTest=2;
  let letTest=1;
  console.log(varTest); //输出2
  console.log(letTest); //输出1
})();

只声明不赋值,变量的默认值是 undefined

const 关键字可以声明不可变变量,同样为块作用域。对不可变的理解在对象上的理解需要注意

const num = 1;
const obj = {
  prop: 'value'
};

num = 2; // Uncaught TypeError: Assignment to constant variable.
obj['prop'] = 'value2';
obj = []; // Uncaught TypeError: Assignment to constant variable.

变量提升

JavaScript中可以引用稍后声明的变量,而不会引发异,这一概念称为变量声明提升(hoisting)

console.log(a); // undefined
var a = 2;



函数

一个函数就是一个可以被外部代码调用(或者函数本身递归调用)的 子程序

定义函数

    1.函数声明
    2.函数表达式
    3.Function 构造函数
    4.箭头函数

function fn(){}

var fn1 = function(){}

var fn2 = new fn('num','obj')

var fn3 = (param) => {}

arguments

    1.arguments:  一个包含了传递给当前执行函数参数的类似于数组的对象
    2.arguments.length: 传给函数的参数的数目
    3.arguments.caller: 调用当前执行函数的函数
    4.arguments.callee: 当前正在执行的函数

function foo() {
  return arguments;
}
var bb=foo(1, 2, 3);     // Arguments[3] 
console.log(bb);         //[1, 2, 3]
console.log(bb.length);  //3

function myFunction(a, b) {
    return arguments.length;
}
document.getElementById("demo").innerHTML = myFunction(4, 3,5);

求最大数

function findMax() {
    var i, max = 0;
    for(i = 0; i < arguments.length; i++) {  //arguments.length等于传参函数的长度 进行循环机制
		if (arguments[i] > max) {    //如果遍历每一个arguments的值大于max就给max赋值
            max = arguments[i];              //遍历第一次2大于0,max=2,第二次14大于max,max=14,第三次6不大于14if等于false不成立max不赋值
        }
    }
    return max;
} 
console.log(findMax(2,14,6,8))//14
计算和

function sumAll() {
    var i, sum = 0;
    for(i = 0; i < arguments.length; i++) {   //arguments.length等于传参函数的长度 进行循环机制
        sum += arguments[i];                  //遍历每一次sum=0+2;2+14......计算出和
    }
    return sum;                               //返回计算值sum
} 
console.log(sumAll(2,14,6,8))//30

函数的参数可以在定义的时候约定默认值

function myFunction(x, y) {
    if (y === undefined) {
        y = 0;
    }    
    return x * y;
}
console.log(myFunction(1,2));//2
console.log(myFunction(1));  //0

//如果y已经定义 , y || 返回 y, 因为 y 是 true, 否则返回 0, 因为 undefined 为 false。
function myFunction(x, y) {
    y=y||0   
    return x * y;
}
console.log(myFunction(1,2));//2
console.log(myFunction(1));  //0

function fn (a = 2, b = 3) {
  return a + b;
}
console.log(fn(2, 3)); // 5
console.log(fn(2)); // 5
console.log(fn()); // 5

JavaScript 函数调用

作为一个函数调用

function myFunction(a, b) {
	return a * b; //调用函数返回参数a*b
}
console.log(myFunction(12, 2)); //24

以上函数不属于任何对象。但是在 JavaScript 中它始终是默认的全局对象。

在 HTML 中默认的全局对象是 HTML 页面本身,所以函数是属于 HTML 页面。

在浏览器中的页面对象是浏览器窗口(window 对象)。以上函数会自动变为 window 对象的函数。

myFunction() 和 window.myFunction() 是一样的:

function myFunction(a, b) {
	return a * b; //调用函数返回参数a*b
}
console.log(window.myFunction(12, 2)); //24

全局对象
当函数没有被自身的对象调用时, this 的值就会变成全局对象。
在 web 浏览器中全局对象是浏览器窗口(window 对象)。
该实例返回 this 的值是 window 对象:

function myFunction() {
	return this; //this 的值是 window 对象:
}
console.log(myFunction()); //Window {external: Object, …}

函数作为方法调用

在 JavaScript 中你可以将函数定义为对象的方法。

以下实例创建了一个对象 (myObject), 对象有两个属性 (firstName 和 lastName), 及一个方法 (fullName):

var myObject = {
    firstName:"John",
    lastName: "Doe",
    fullName: function() {
		return this.firstName + " " + this.lastName;
    }
}
console.log(myObject.fullName()); //John Doe

fullName 方法是一个函数。函数属于对象。 myObject 是函数的所有者。

this对象,拥有 JavaScript 代码。实例中 this 的值为 myObject 对象。

var myObject = {
    firstName:"John",
    lastName: "Doe",
    fullName: function() {
		return this;            
    }
}
console.log(myObject.fullName()); //返回 [object Object] (所有者对象)

使用构造函数调用函数

如果函数调用前使用了 new 关键字, 则是调用了构造函数。

这看起来就像创建了新的函数,但实际上 JavaScript 函数是重新创建的对象:

// 构造函数:
function myFunction(arg1, arg2) {
    this.firstName = arg1;
    this.lastName  = arg2;
}
var x = new myFunction("John","Doe");
console.log(x.firstName);  // 返回 "John" 
构造函数的调用会创建一个新的对象。新对象会继承构造函数的属性和方法。

构造函数中 this 关键字没有任何的值。
this 的值在函数调用时实例化对象(new object)时创建。


作为函数方法调用函数

在 JavaScript 中, 函数是对象。JavaScript 函数有它的属性和方法。

call() 和 apply() 是预定义的函数方法。 两个方法可用于调用函数,两个方法的第一个参数必须是对象本身。

function myFunction(a, b) {
    return a * b;
}
console.log(myFunction.call(myFunction, 10, 2));      // 返回 20 

function myFunctions(a, b) {
    return a * b;
}
myArray = [10,2];
console.log(myFunctions.apply(myFunctions,myArray));  // 返回 20

两个方法都使用了对象本身作为第一个参数。两者的区别在于第二个参数:apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入,而call则作为call的参数传入(从第二个参数开始)。

在 JavaScript 严格模式(strict mode)下, 在调用函数时第一个参数会成为 this 的值, 即使该参数不是一个对象。

在 JavaScript 非严格模式(non-strict mode)下, 如果第一个参数的值是 null 或 undefined, 它将使用全局对象替代。

通过 call() 或 apply() 方法你可以设置 this 的值, 且作为已存在对象的新方法调用。

JavaScript 闭包

JavaScript 变量可以是局部变量或全局变量。

私有变量可以用到闭包。

全局变量

函数可以访问函数内部定义的变量

function myFunction() {
    var a = 4;           //函数可以访问函数内部定义的变量
    console.log( a * a); //16
}
myFunction();  //调用函数

函数也可以访问函数外部定义的变量

var a = 4;
function myFunction() {
	console.log( a * a); //16
} 
myFunction();  //调用函数

后面一个实例中, a 是一个 全局 变量。

在web页面中全局变量属于 window 对象。

全局变量可应用于页面上的所有脚本。

在第一个实例中, a 是一个 局部 变量。

局部变量只能用于定义它函数内部。对于其他的函数或脚本代码是不可用的。

全局和局部变量即便名称相同,它们也是两个不同的变量。修改其中一个,不会影响另一个的值。

变量声明是如果不使用 var 关键字,那么它就是一个全局变量,即便它在函数内定义。

JavaScript 内嵌函数

所有函数都能访问全局变量。  

实际上,在 JavaScript 中,所有函数都能访问它们上一层的作用域。

JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。

该实例中,内嵌函数 plus() 可以访问父函数的 counter 变量:

function add() {
	var counter = 0;
    function plus() {counter += 1;}
    plus();    
    return counter; 
}
console.log(add());  //1
console.log(add());  //1

JavaScript 闭包

<p>局部变量计数。</p>
<button type="button" οnclick="myFunction()">计数!</button>
<p id="demo">0</p>
<script>
var add = (function () {
    var counter = 0;
    return function () {return counter += 1;}
})();
function myFunction(){
    document.getElementById("demo").innerHTML = add();
}
</script>

解析

变量 add 指定了函数自我调用的返回字值。

自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。

add变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。

这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。

计数器受匿名函数的作用域保护,只能通过 add 方法修改。

闭包是可访问上一层函数作用域里变量的函数,即便上一层函数已经关闭。










每个函数都有一个 prototype 的对象属性,对象内有一个 constructor 属性,默认指向函数本身
每个对象都有一个 __proto__ 的属性,属相指向其父类型的 prototype

function oo(){
    this.x=1;
}
console.log(oo.prototype);


function oo(){
    this.x=1;
}
console.log(oo.prototype);

var gg=new oo();    //构造函数
console.log(gg.__proto__);




function oo(a){
    this.x=a;
}
oo.prototype.print = function () {
  console.log(this.x);
};
var gg=new oo(1);    //构造函数
gg.print();

console.log(oo.prototype);
console.log(gg.__proto__);
console.log(gg.print.prototype);





对象

JavaScript 中对象是可变 键控集合 (keyed collections)
定义对象


   1, 字面量
   2,构造函数

var obj = {
  prop: 'value',
  fn: function(){this.l=2}
};
var date = new Date();

构造函数

构造函数和普通函数并没有区别,使用 new 关键字调用就是构造函数,使用构造函数可以 实例化 一个对象

函数的返回值有两种可能

    显式调用 return 返回 return 后表达式的求值
    没有调用 return 返回 undefined

function People(name, age) {
  this.name = name;
  this.age = age;
}

var people = new People('Byron', 26);

构造函数返回值

    1,没有返回值
    2,简单数据类型
    3,对象类型

前两种情况构造函数返回构造对象的实例,实例化对象正是利用的这个特性

第三种构造函数和普通函数表现一致,返回 return 后表达式的结果
prototype

    1,每个函数都有一个 prototype 的对象属性,对象内有一个 constructor 属性,默认指向函数本身
    2,每个对象都有一个 __proto__ 的属性,属相指向其父类型的 prototype

function Person(name) {
  this.name = name;
}

Person.prototype.print = function () {
  console.log(this.name);
};

var p1 = new Person('Byron');
var p2 = new Person('Casper');

p1.print();
p2.print();

this 和作用域

作用域可以通俗的理解

    我是谁
    我有哪些马仔

其中我是谁的回答就是 this

马仔就是我的局部变量
this 场景

普通函数

    严格模式:undefined
    非严格模式: 全局对象
    Node: global
    浏览器: window

构造函数:对象的实例

对象方法:对象本身
call & apply

    fn.call(context, arg1, arg2, …, argn)
    fn.apply(context, args)

function isNumber(obj) {
  return Object.prototype.toString.call(obj) === '[object Number]';
}

Function.prototype.bind

bind 返回一个新函数,函数的作用域为 bind 参数

function fn() {
  this.i = 0;

  setInterval(function () {
    console.log(this.i++);
  }.bind(this), 500)
}

fn();

() => {}

箭头函数是 ES6 提供的新特性,是简写的 函数表达式,拥有词法作用域和 this 值

function fn() {
  this.i = 0;

  setInterval(() => {
    console.log(this.i++);
  }, 500)
}

fn();

继承

在 JavaScript 的场景,继承有两个目标,子类需要得到父类的:

    对象的属性
    对象的方法

function inherits(child, parent) {
  var _proptotype = Object.create(parent.prototype);
  _proptotype.constructor = child.prototype.constructor;
  child.prototype = _proptotype;
}

function People(name, age) {
  this.name = name;
  this.age = age;
}

People.prototype.getName = function () {
  return this.name;
}

function English(name, age, language) {
  People.call(this, name, age);
  this.language = language;
}

inherits(English, People);

English.prototype.introduce = function () {
  console.log('Hi, I am ' + this.getName());
  console.log('I speak ' + this.language);
}

function Chinese(name, age, language) {
  People.call(this, name, age);
  this.language = language;
}

inherits(Chinese, People);

Chinese.prototype.introduce = function () {
  console.log('你好,我是' + this.getName());
  console.log('我说' + this.language);
}

var en = new English('Byron', 26, 'English');
var cn = new Chinese('色拉油', 27, '汉语');

en.introduce();
cn.introduce();

ES6 class 与继承

"use strict";

class People{
  constructor(name, age){
    this.name = name;
    this.age = age;
  }

  getName(){
    return this.name;
  }
}

class English extends People{
  constructor(name, age, language){
    super(name, age);
    this.language = language;
  }

  introduce(){
    console.log('Hi, I am ' + this.getName());
    console.log('I speak ' + this.language);
  }
}

let en = new English('Byron', 26, 'English');

en.introduce();

语法
label statement

loop:
    for (var i = 0; i < 10; i++) {
      for (var j = 0; j < 5; j++) {
        console.log(j);
        if (j === 1) {
          break loop;
        }
      }
    }

console.log(i);

语句与表达式

var x = { a:1 };

{ a:1 }

{ a:1, b:2 }

立即执行函数

( function() {}() );
( function() {} )();
[ function() {}() ];

~ function() {}();
! function() {}();
+ function() {}();
- function() {}();

delete function() {}();
typeof function() {}();
void function() {}();
new function() {}();
new function() {};

var f = function() {}();

1, function() {}();
1 ^ function() {}();
1 > function() {}();

高阶函数

高阶函数是把函数当做参数或者返回值是函数的函数
回调函数

[1, 2, 3, 4].forEach(function(item){
  console.log(item);
});

闭包
闭包由两部分组成

    函数
    环境:函数创建时作用域内的局部变量

function makeCounter(init) {
  var init = init || 0;

  return function(){
    return ++init;
  }
}

var counter = makeCounter(10);

console.log(counter());
console.log(counter());

典型错误

for (var i = 0; i < doms.length; i++) {
  doms.eq(i).on('click', function (ev) {
    console.log(i);
  });
}

for (var i = 0; i < doms.length; i++) {
  (function (i) {
    doms.eq(i).on('click', function (ev) {
      console.log(i);
    });
  })(i);
}

惰性函数
function eventBinderGenerator() {
  if (window.addEventListener) {
    return function (element, type, handler) {
      element.addEventListener(type, hanlder, false);
    }
  } else {
    return function (element, type, handler) {
      element.attachEvent('on' + type, handler.bind(element, window.event));
    }
  }
}

var addEvent = eventBinderGenerator();

柯里化

一种允许使用部分参数生成函数的方式

function isType(type) {
  return function(obj){
    return Object.prototype.toString.call(obj) === '[object '+ type +']';
  }
}

var isNumber = isType('Number');

console.log(isNumber(1));
console.log(isNumber('s'));

var isArray = isType('Array');

console.log(isArray(1));
console.log(isArray([1, 2, 3]));

function f(n) {
  return n * n;
}

function g(n) {
  return n * 2;
}

console.log(f(g(5)));

function pipe(f, g) {
  return function () {
    return f.call(null, g.apply(null, arguments));
  }
}

var fn = pipe(f, g);

console.log(fn(5));

尾递归
  1. 尾调用是指某个函数的最后一步是调用另一个函数
  2. 函数调用自身,称为递归
  3. 如果尾调用自身,就称为尾递归

递归很容易发生"栈溢出"错误(stack overflow)

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120

但对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

柯里化减少参数

function currying(fn, n) {
  return function (m) {
    return fn.call(this, m, n);
  };
}

function tailFactorial(n, total) {
  if (n === 1) return total;
  return tailFactorial(n - 1, n * total);
}

const factorial = currying(tailFactorial, 1);

factorial(5) // 120

反柯里化
Function.prototype.uncurry = function () {
  return this.call.bind(this);
};

push 通用化

var push = Array.prototype.push.uncurry();

var arr = [];

push(arr, 1);
push(arr, 2);
push(arr, 3);

console.log(arr);



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值