写一个debounce函数

这里尝试写一个debounce函数的过程。

debounce是什么?就是防抖动,假设我们监听了输入框的change事件,绑定了一个函数来验证,那么我们在输入的过程中就会不断触发这个函数,很多时候我们其实不想这个函数触发得这么频繁,因为用户都是不断输入的,没必要不断验证,我们真实希望的是,用户停止的时候才触发这个函数,所以我们希望给一个delay的时间,在这个时间内,不断触发的函数,也只按最后一次来触发。

就好像上图那样,只要你触发后面,如果不超过delay时间再次触发的话,也会继续等,直至最后一次触发超过delay的时间,函数才执行一次,很好理解。

当然了,如果希望的是立即执行函数的话,情况就会是下面这种情况:

只要你触发的时间比上次函数执行时间超过了delay的时间,就会被立即执行。

这里写的debounce函数就不考虑立即执行这种情况,按前面的情况来写一下。

首先,这个函数名称就叫做debounce,这个函数接受两个参数,一个是防抖执行的函数,一个是延时delay的时间,所以我们会用到setTimeout这个方法,而且我们执行了debounce之后需要返回一个被触发的函数,考虑到我们需要,会先有:

function debounce(fn, delay) {
  let _fn = fn  //保存函数的引用
  return function(){
    let _self = this
    let args = arguments
    setTimeout(function(){
      _fn.apply(_self, args)
    }, delay)
  }
}

这样执行下来,也只不过每个函数都执行一次延时而已,还没达到我们想要的效果,所以我们还需要做的是,这个函数在delay时间内,也就是还没执行前,再次触发的话,就将上一次设定的延时器清除,那这就容易增加代码为:

function debounce(fn, delay) {
  let _fn = fn  //保存函数的引用  
  let timer
  return function(){
    clearTimeout(timer)
    let _self = this
    let args = arguments
    timer = setTimeout(function(){
      _fn.apply(_self, args)
    }, delay)
  }
}

这就基本实现了debounce函数了,其实就是利用了一个闭包的形式,将原本的timer保存起来,只要函数还没执行(也就是delay时间内)再次触发的话,就会先清除了前一个的timer了,基本实现了防抖的效果。

js创建对象的7种模式

其实我们在新建一个对象的时候,通常会用什么方法呢?我觉得,大多数的人都会这样新建,简单直接:

var myObject = {
  name:'test',
  content:'hello world',
  do:function(){console.log('nothing')}
}

在这里,以上面例子来详细说说js创建对象的7种模式:

第一种:工厂模式

function createObject(name, content){
  return {
    name,
    content,
    do:function(){console.log('nothing')}
  }
}

var myObject = createObject('test', 'hello world')

这种模式非常直观,丢进去,丢出来。

第二种:构造函数模式

function createObject(name, content){
  this.name = name
  this.content = content
  this.do = function(){console.log('nothing')}
}

var myObject = new createObject('test', 'hello world')

这种模式也很常用,定义了一个构造函数,然后对象都是new出来的。

第三种:原型模式

function createObject(){
}
createObject.prototype.name = 'test'
createObject.prototype.content = 'hello world'
createObject.do = function(){console.log('nothing')}
var myObject = new createObject()

这种模式相对构造函数模式,是在它的原型上直接创建属性,后面new出来的对象就会继承下来这些属性和方法。

第四种:构造函数模式和原型模型组合

function createObject(name, content){
  this.name = name
  this.content = content
}
createObject.prototype = {
  constructor: createObject,
  do:function(){console.log('nothing')}
}
var myObject = new createObject('test', 'hello world')

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性,结果,每个实例都会有一个自己的一份实例属性和方法,但同时共享着对方法的引用,最大限度地节省了内存,是目前使用最广泛的一种创建自定义类型的方法。

第五种:动态原型模式

function createObject(name, content){
  this.name = name
  this.content = content
  if(typeof this.do != "function"){
    createObject.prototype.do = function(){console.log('nothing')}
  }
}

var myObject = new createObject('test', 'hello world')

这里就是判断是否存在do这个方法前提下动态在原型上添加,而且只在调用函数的时候才会执行,可以说是非常好的一种模式。

第六种:寄生构造函数模式

function SpectialArray(){
  var values = new Array()
  values.push().apply(values, arguments)
  values.toPipedString = function(){
    return this.join('|')
  }
  return values
}

var colors = new SpecialArray('red', 'blue', 'green')
alert(colors.toPipedString())  //"red|blue|green"

其实这种模式也很容易理解,我们不能直接修改Array构造函数,就借用来寄生一下,添加我们需要的属性。

第七种:稳妥构造函数模式

function createObject(name){
  var o = new Object()
  o.do = function(){console.log(name)}
  return o
}
var myObject = createObject('name')

看起来有点熟对吧,其实不完全熟,它其实是通过myObject的方法才能访问到数据成员的这样一种方式,没有其他方式能够访问,myObject就是保存着一个稳妥的对象。

好了,关于js创建对象的7种模式,大抵如上。

混杂模式与标准模式的一些区别

关于文档模式,已在上一篇文章(文档模式)中说过,这里就说说混杂模式和标准模式的一些区别。

众所周知的一个区别,就是CSS盒模型了,我们都知道标准模式中,CSS盒模型是w3c标准盒模型,也就是它的height和width就是里面内容(content)的高和宽。

那混杂模式呢?它会按照IE5.5的CSS盒模型,就是它的height和width是包含padding和border的宽度的,这个就是非常明显的区别,当然了,IE8及之前只支持IE盒模型,到IE8+之后,就可以通过设置box-sizing的值来切换,默认值是content-box,就是w3c标准盒模型,而设置为border-box,就是回到IE盒模型了。

基本来说,就如图:

一个div元素里面只包含图片的时候,混杂模式和标准模式也是不一样的,在标准模式下,不管IE还是标准浏览器,在图片底部都有3像素的空白。但在混杂模式下,标准浏览器(Chrome)中div距图片底部默认没有空白。

如果是在IE的混杂模式下,给行内元素是可以设置高度和宽度的,标准模式下肯定是无效的。

在混杂模式下,IE和其他浏览器对百分比宽度的解析是不一样的。如果父级是行内块或者浮动或者有定位的元素,给子元素设置百分比宽度100%时,IE的混杂会以父级的100%算(父级没有设置宽度,再往上一层),而标准浏览器是取决于内容的宽度,所以关键还是看父级是否行内块或者浮动或者有定位的元素。

在混杂模式下,当我们给一个元素设百分比高度,其他浏览器(正常,inline高度无变化,inline-block和block都会按百分比),而IE是自适应到内容高度。有点跟上面设置宽度相反的感觉。

overflow溢出默认值的问题。标准模式下,溢出元素是溢出可见的,超出部分的内容呈现在它的包含元素外。在混杂模式下,IE浏览器的溢出元素会自适应内容的尺寸。其实这个联想上面也很容易理解,标准模式是定好了的,而混杂模式是靠内容来撑开的感觉。

这里主要参考了这篇总结得不错的文章:标准模式与混杂模式

 

文档模式

IE5.5引入了文档模式的概念,最初的文档模式包含两种:混杂模式和标准模式。而这个模式就是通过文档类型(doctype)的切换来实现的。

为什么会出来这个东西呢?其实浏览器发展以前,都是自己弄自己的这样一个境况,所以很多时候不同浏览器解析出来的东西大不相同,后来有了w3c标准,将相应的规范统一了起来,大家就有了一定的标准了,所以其实标准模式就是标准后的模式,而混杂模式就是保持IE5以前的行为。后来IE又提出了一种叫做准标准模式的东西,其实就是大部分按照标准,但不完全按照,这里具体就不展开这个准标准模式了。

如果文档开始的时候没有引入文档类型声明,所有的浏览器默认都会开启混杂模式的,混杂模式在不同的浏览器肯定行为差异巨大的,很多时候要用一些hack的技术,所以并不推荐这种方式,一般都开启标准模式。

那标准模式如何开启呢?就是在文档的最前面加上<!DOCTYPE html>这么一句就行了,什么意思呢?DOCTYPE,或者称为 Document Type Declaration(文档类型声明,缩写 DTD),这样,在浏览器解析 HTML 文档正文之前就可以确定当前文档的类型,以决定其需要采用的渲染模式。

我们怎么用js判断是开启了哪个模式呢?可以通过document.compatMode这个属性,如果是CSS1Compat就是标准模式,如果是BackCompat就是混杂模式。

至于混杂模式和标准模式会有什么巨大差异呢?下一篇再说说。

 

Content-Type

说Content-Type之前,先说一下MIME,什么是MIME呢?

百科一下,MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开,它是一个互联网标准,这个标 准被定义在RFC 2045、RFC 2046、RFC 2047、RFC 2048、RFC 2049等RFC中。 MIME改 善了由RFC 822转变而来的RFC 2822,这些旧标准规定电子邮件标准并不允许在邮件消 息中使用7位ASCII字符集以外的字符。正因如此,一些非英语字符消息和二进制文件 ,图像,声音等非文字消息原本都不能在电子邮件中传输(MIME可以)。MIME规定了用 于表示各种各样的数据类型的符号化方法。 此外,在万维网中使用的HTTP协议中也使 用了MIME的框架,标准被扩展为互联网媒体类型。

前面的文字啰嗦了一点,简单来说,MIME就是表示的一种类型,内容由两部分组成, 前面是数据的大类别,后面定义具体的种类。例如,image/jpeg就是表示jpeg图像类型。

而应用在HTTP中,MIME类型被定义在请求头和响应头的headers中,用Content-Type字段表示,所以我们可以知道HTTP中,Content-Type表示的是一种MIME类型。

响应(response)的头信息,Content-Type表示的是返回的文件类型,这个容易理解,那请求(request)头信息上面的Content-Type呢?代表的是什么呢?

一般,我们在POST/PUT方法的时候,会指定请求头的Content-Type,会有那么的几种类型:

application/x-www-form-urlencoded:这种是最常用的类型,其实就是key=value形式的字符串拼接,例如:key1=value1&key2=value2&key3=value3

multipart/form-data:这种在自己new Formdata()的时候,或者上传文件的时候会用到,会有特别分隔字符串,然后跟着name是什么,对应的value是什么,里面是文件的,还有另一个Content-Type来表明类型,例如:

// request body
–Boundary———————–
content-disposition: form-data; name=”field1″

value1
–Boundary———————–
content-disposition: form-data; name=”field2″

value2
–Boundary———————–
Content-Disposition: form-data; name=”file”; filename=”chrome.png”
Content-Type: image/png
Content-Transfer-Encoding: binary

$binarydata
–Boundary———————–
Content-Disposition: form-data; name=”file1″; filename=”a.txt”
Content-Type: text/plain

application/json:这种类型是现在很常用的一种,用于传递json结构的数据,很适合前后端分离的数据传递。

text/xml:XML-RPC(XML Remote Procedure Call),是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范。xml相比json太过臃肿,一般用application/json会更加灵活。

 

 

BOM

BOM,不是炸弹爆炸。

BOM,全称是Browser Object Model,即浏览器对象模型,前面(dom的几个级别)已经说过DOM了,那么BOM又是什么来的呢?

其实BOM到现在,还没有正式的标准,但是现在的浏览器在实现js交互方面都提供了相同的方法和属性,所以这些会被认为是BOM的方法和属性。

一张简单的图:

从图里面大概可以认清楚什么是BOM,什么是DOM,和它们之间的关系。

这里就举些简单的例子:

我想获得浏览器窗口的高度:window.innerHeight

我想获得可用的屏幕宽度:screen.availWidth

我想获得当前页面的url:location.href

我想跳转到页面历史的前一个url:history.back()

我想获得浏览器类型:navigator.userAgent

上述等等,其实就是各大对象包含的一些方法和属性,有空就打开调试窗口,打印看看,就什么都知道了。

XML和HTML

前面的文章(dom的几个级别)说到,DOM其实包含DOM XML的,那XML是什么东西呢?和HTML又有什么关系呢?

XML 全称是可扩展标记语言,是被设计用来传输和存储数据。
HTML 全称超文本标记语言,是被设计用来显示数据。

XML是标准通用置标语言(SGML)的一个子集,而HTML是SGML的一个应用,XML就是要弥补HTML的不足,所以是未来大规模应用的趋势,而中间会有个叫做XHTML(可扩展超文本标记语言)的东西,就是要支撑HTML过渡到XML的产物,目标是替代HTML,而XHTML说得简单点,就是要求更严格的HTML。

在 XML 中,元素名称是由开发者定义的,当两个不同的文档使用相同的元素名时,就会发生命名冲突。所以前面讲DOM级别的时候,有一个叫做XML命名空间的东西,其实就是将某个相同元素区分在对应的命名空间中。

最后再提提XML的几个语法规则,就知道为什么说XHTML是更严格的HTML了:

XML 文档必须有根元素
XML 文档必须有关闭标签
XML 标签对大小写敏感
XML 元素必须被正确的嵌套
XML 属性必须加引号

dom的几个级别

关于DOM的几个级别,其实都是来源于W3C标准,W3C就是World Wide Web Consortium的缩写,中文翻译过来就是万维网联盟,是Web技术领域最具权威和影响力的国际中立性技术标准机构,W3C标准其实包含一系列标准(结构、表现、行为),具体就不展开了。

其中DOM就是它的标准之一,DOM分为了三个级别:DOM1级、DOM2级、DOM3级。

那DOM具体是什么东西呢?DOM其实是文档对象模型(Document Object Model)的缩写,它是一种提供对文档访问或修改方法的模型,它的范围很广,但是对于web开发者来说,往往都认为它是指javascript在浏览器上访问和修改html文档的一种技术,但是实际上它的范围远不局限在这里。

DOM1级(DOM Level 1)于1998年10月成为W3C的推荐标准,DOM1级由两个模块组成:DOM核心(DOM Core)和DOM HTML,DOM Core能映射以XML为基础的文档结构,允许获取和操作文档的任意部分,而DOM HTML通过添加HTML专用的对象与函数对DOM Core进行了扩展。简单来说,DOM1级就是映射文档结构和提供基本的文档操作方法。

DOM2级,就是对DOM1级进行扩展,2级DOM通过对象接口增加了对鼠标和用户界面事件(DHTML长期支持鼠标与用户界面事件)、范围、遍历(重复执行DOM文档)和层叠样式表(CSS)的支持。同时也对DOM 1的核心进行了扩展,从而可支持XML命名空间。简单来说,DOM2级就是在DOM1的基础上增加了视图、事件、样式、遍历和范围的接口,和支持XML命名空间。

DOM3级,在前面DOM的基础上,引入了以统一方式加载和保存文档的方法,新增了验证文档的方法,同时也对DOM核心进行了扩展,开始支持XML 1.0规范。

除了上面的三个等级之外,还有一个叫做DOM0级的东西,实际上标准并没有这个东西,它指的是IE4和Netscape Navigator 4.0最初支持的DHTML,DHTML实际上是HTML、CSS和JS的一个集成,代表的是一种已有的技术,不是标准,所以DOM0级其实代表的是历史节点中未形成标准的一个初期产物。

举个常见的DOM0级事件和DOM2级事件的比较:绑定的按钮的onclick赋值为一个函数就是DOM0级的,但是onclick多次赋值不同函数,最后也会被后面的函数覆盖掉;而DOM2级中利用提供的addEventListener方法监听按钮的click事件,多次写监听同一个事件,函数会被依次执行的,不会被覆盖。

关于DOM几个级别的具体内容,还得细看。