before

机会来的措手不及,但一番聊天后思考了很多。实习时候用多了jq,现在做毕设也用的框架,并且用框架的时候,也一部分只知道如何用,而没去究其原理,觉得自己一直认为重要的基础却被抛下了。这显得自己的简历中“重视基础”写的冠冕堂皇,于是重读高程3(其实以前没看多少,更喜欢读你不知道的js系列)。

希望这次经历后自己重拾初衷,端正作为一个工程师应有的态度,不能浮躁,毕竟理想是成为一名写一手好代码的人。

生疏点的记录

零散

  • ie10+支持严格模式

定义 赋值

1
2
3
4
5
//var c = 1; //1,1,1
var a = b = c;
console.log(a,b,c)
//a = 1; // c not defined 这里是赋值把c赋值给前面的 所以未定义
//var c = 1; //undefined undefined 1 c预定义了 但是在上方的时候是没赋值 所以undefined

数据类型

1.nulltypeofobject

1
2
3
//判断是否是null
var a = null;
if(!a && a typeof === 'object'){...}

2.Number类型中八进制,十六进制的表示(严格模式下错误)

1
2
var num1 = 070; //八进制的56
var num2 = 0xA; //十六进制的10

3.js中可以保存正零(+0)和负零(-0)

1
2
3
var num1 = -0;
var num2 = +0;
console.log(num1 === num2); //true

4.浮点数精度不如整数 0.1+0.2结果不是0.3 是0.30000000000000004,但0.15和0.15就是0.3,所以不要测试浮点数值(IEEE754数值浮点计算的锅)

5.NaN大话题

  • 和任何值都不等(包括自身) 可以利用这点在实际开发中做判断
  • 正数/0返回Infinity,负数则-Infinity,0/0才返回NaN

操作符

  1. && 逻辑与,是个短路操作符(||也是短路操作符)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    var found = true;
    var result = found && somevariable; //直接报错 因为没定义sv
    console.log(result);
    var found1 = false;
    var rs1 = found1 && somevariable;
    console.log(rs1); //false,不会发生错误
    // ||中也相似 如果后面的undefined 前面的是true则不报错 false则报错
  2. ~

面向对象

1.对象

数据属性

修改对象数据值属性的特性,使用 Object.defineProperty() 方法

1
2
3
4
5
6
Object.defineProperty(person,"name",{
writable:true, //可写
value:"huk", //属性的值
configurable:true, //可配置 改成false后就不能删除 修改这个属性(除了writable可以改成false)
enumerable:true //可枚举
})
访问器属性

不包含数据值,包含 gettersetter 函数。读取属性的时候调用get,写入属性的时候调用set。

这时设置一个属性会导致别的属性变化

1
2
3
4
5
6
7
8
9
10
var book = {
_year:2004,
...
}
Object.defineProperty(book,"year",{
get:function(){
return this._year
},
set:function(newValue){...}
})
定义多个属性
1
2
3
4
5
6
7
8
9
10
11
Object.defineProperties(book,{
_year:{
value:2004
},
year:{
get:function(){...},
set:function(){...}
}
})
//读取属性的特性
Object.getOwnPropertyDescriptor(book,'_year');
创建对象
  • 工厂模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function foo(name, age) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function () {
    console.log(this.name);
    };
    return o;
    }
    var per = foo('huk',23);

    缺点:没有解决对象识别问题

  • 构造函数模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function Foo2(name, age) {
    this.name = name;
    this.age = age;
    this.sayName = sayName();
    }
    function sayName(){
    console.log(this.name)
    }
    var per1 = new Foo2('huk1',23);
    var per2 = new Foo2('huk2',22);

    创建Foo2构造函数的实例,需要使用new操作符。per1和per2的constructor属性都指向Foo2,都是Foo2的实例。

    缺点:全局作用域中定义的函数实际只给某个对象使用,如果要很多方法,就要定义很多全局下的函数,没有封装的意义。

  • 原型模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function Foo3() {}
    Foo3.prototype.name = 'huk3';
    Foo3.prototype.age = 21;
    Foo3.prototype.sayName = function () {
    console.log(this.name);
    };
    var per = new Foo3();
    per.sayName(); //huk3
    var per2 = new Foo3();
    per2.sayName(); //huk3
    //per.sayName == per2.sayName true
  • Foo3原型对象的 constructor 属性 –> Foo3构造函数

  • 创建的实例per和per2的 [[prototype]] 指向Foo3.prototype

    • [[prototype]]无法访问,但可以用 isPrototype() 方法判断是否指向对象的prototype
    • 实例上定义新的属性(和原型同名的属性)会屏蔽原型上的属性, hasOwnProperty() 可以检测是否是实例上定义的值
    • in操作符 "name" in person,原型和实例上的属性都遍历
    • Object.keys():对象上所有可枚举的实例属性
    • Object.getOwnPropertyNames():对象上所有可or不可枚举的实例属性

    缺点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function Person(){}
    Person.prototype = {
    //这边prototype重写后,原来的链断了,所以要重新指向person。这时候Person构造函数上其实是有两个prototype对象的,此后定义的实例都指向新的,重写之前的仍指向旧的
    constructor:Person,
    name:'huk',
    friends:["huk1","huk2"]
    //....
    };
    var per1 = new Person();
    var per2 = new Person();
    per1.friends.push('huk3');
    //这时 per1.friends === per2.friends "huk1,huk2,huk3"

  • 原型和构造组合使用

    实例的属性通过构造函数定义,共享的属性和方法使用原型定义。

    1
    2
    3
    4
    5
    6
    7
    8
    function Person(name,age){
    this.name = name;
    this.age = age;
    }
    Person.prototype = {
    constructor:Person,
    sayName: function(){console.log(this.name)}
    }

    各实例之间的name和age属性互相不干扰

  • 寄生构造函数模式

2.继承

寄生继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Person() {
this.eyes = 2;
}
Person.prototype.sayHi = function () {
console.log('hi')
};
Person.prototype.born = function () {
this.sayHi();
console.log('has born')
};
function girl() {
var girl = new Person();
girl.sex = 'girl';
var born = girl.born;
girl.born = function () {
born.call(this);
console.log('sex is '+this.sex);
};
return girl;
}
var kid = new girl();
kid.born();// hi / has born / sex is girl

Dom

  • getElementsByClassName()兼容IE9+
  • querySelector()兼容IE8+
  • document.achiveElement获取焦点元素,document.hasFocus()得到文档是否获得焦点
  • data-type在原生中的设置是ele.dataset.type = ‘…’

文档模式

过渡型或框架型HTML声明与过渡型或框架型XHTML声明均可使浏览器进入近似标准模式,同时,html5的DOCTYPE声明和严格型HTML声明以及严格型XHTML声明则会使浏览器进入标准模式。

不写DOCTYPE则进入混杂模式

1
2
//判断文档模式
document.compatMode == "CSS1Compat"?"标准模式":"混杂模式"

事件

dom事件流

三个阶段:事件捕获、处于目标阶段、事件冒泡阶段

实际:捕获->接收到event->冒泡响应event返回到文档

ie8及以前版本不支持dom事件流

dom0级事件处理

给元素指定事件处理程序属性,给属性设置函数

1
2
3
4
5
6
7
//创建
var btn = document.getElementbById('btn');
btn.onclick = function(){
console.log(this.id); //"btn"
}
//删除
btn.onclick = null;
dom2级事件处理程序

第三个参数为true的话代表捕获阶段调用事件处理程序,false代表冒泡阶段调用。

如果一个元素上定义多个click事件,从定义的从上至下顺序执行。

1
2
3
4
5
6
//创建
var btn = document.getElementbById('btn');
var handler = function(){console.log(this.id);}
btn.addEventListener('click',handler,false);
//删除
btn.removeEventListener('click',handler,false);

删除的时候需要删除完全相同的,所以要定义个函数表达式

ie8及以下不支持dom2事件处理

IE事件处理程序

作用域在全局上,所以 this === window

早起ie只支持事件冒泡,所以事件被添加到冒泡阶段

果一个元素上定义多个click事件,从定义的从下至上的相反顺序执行。

1
2
3
4
5
6
//创建
var btn = document.getElementbById('btn');
var handler = function(){console.log("ok")}
btn.attachEvent('click',handler);
//删除
btn.detachEvent('click',handler);
dom中的事件对象
1
2
3
//给目标元素指定事件处理程序时(在第二阶段时)
ev.currentTarget === this;
ev.target === this;
事件对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//同时兼容ie的处理
btn.onclick = function(ev){
var ev = ev || window.event;
//阻止默认行为
if(ev.preventDefault){
ev.preventDefault();
}else{
ev.returnValue = false;
}
//阻止冒泡
if(ev.stopPropagation){
ev.stopPropagation();
}else{
ev.cancelBubble = true;
}
}