定义

”函数“ 和 ”函数内部能访问到的变量“的总和,就是一个闭包。

作用

函数内部可以使用函数外部的变量,那函数外部怎样使用函数内部的变量呢?
闭包的最大用处是可以

  • 读取隐藏在函数内部的变量
  • 读取的过程也会让这些变量始终保持在内存里,不会被回收

示例

1
2
3
4
5
6
7
8
9
10
function foo(){
var local = 1
function bar(){
local++
return local
}
return bar
}
var func = foo()
func()

local 变量和 bar 函数就组成了一个闭包(Closure)。
retrun bar就是使用了这个闭包,达到了访问隐藏变量的效果。

1
2
3
4
5
6
7
8
9
function createIncrementor(start){
return function(){
return start++
}
}
var inc = createIncrementor(5)
inc() //5
inc() //6
inc() //7

function和start形成了一个闭包
return了这个function使用了闭包,可以让start一直存活在内存中。

总结

函数和变量形成一个闭包,return这个函数就使用了它。
闭包是 JS 函数作用域的副产品,它的作用大于概念。

关于像素

  • css中的1px并不等于设备的1px
  • 显示器上的物理像素等于显示器的点距
  • css中的像素只是一个抽象的单位,在不同的设备或不同的环境中,css中的1px所代表的设备物理像素是不同的。

关于meta viewport

  • 两种viewport:layout viewport 和 visual viewport
    两种viewport
  • visual viewport可以类比为移动设备屏幕的大小范围,而layout viewport要大很多。
  • 移动设备上的浏览器大多都会把自己默认的layout viewport设为980px,用户的设备很小,这样带来的后果就是浏览器会出现横向滚动条。(这个layout viewport的宽度可以通过 document.documentElement.clientWidth 来获取)
  • 通过这个默认的layout viewport,所有pc的网页在手机上都可以浏览啦,只不过非常难用,用户要不断的滑动页面才能看到整个网页。
  • 很明显,这不是我们想要的移动端网页。所以,越来越多的网站都会为移动设备进行单独的设计,那怎么样设计移动设备的页面呢?

首先,准备工作。

修改默认的layout viewport,告别乱划乱放大的浏览方式。

<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  • content=”width=device-width: 让layout viewport等于设备的理想宽度
  • 查看各机型的理想viewport宽度
  • user-scalable=no:禁止用户进行缩放
  • initial-scale=1.0:设置页面的初始缩放值为1
  • maximum-scale=1.0:允许用户的最大缩放值为1
  • minimum-scale=1.0:允许用户的最小缩放值为1
  • 这个标签是移动端适配的前提,禁止浏览器缩放网页,也禁止用户缩放网页,为什么禁止呢,因为我们会给移动端写一个完美的展示效果,根本不需要缩放!

如何实现移动端适配?

有两种常用的适配方案:

1. 通过媒体查询的方式即CSS3的meida queries

  • 它主要是通过查询设备的宽度来执行不同的 css 代码,最终达到界面的配置。核心语法是:
1
2
3
@media screen and (max-width: 600px) { /*当屏幕尺寸小于600px时,应用下面的CSS样式*/
/* css代码*/
}
  • 优点
  1. media query可以做到设备像素比的判断,方法简单,成本低,特别是对移动和PC维护同一套代码的时候。
  2. 图片便于修改,只需修改css文件
  3. 调整屏幕宽度的时候不用刷新页面即可响应式展示
  • 缺点
  1. 代码量比较大,维护不方便
  2. 为了兼顾大屏幕或高清设备,会造成其他设备资源浪费,特别是加载图片资源
  3. 为了兼顾移动端和PC端各自响应式的展示效果,难免会损失各自特有的交互方式

2. rem缩放

  • 根据屏幕宽度设定 rem 值,需要适配的元素都使用 rem 为单位,不需要适配的元素还是使用 px 为单位。
  • 使用js动态调整rem
1
2
3
4
5
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<script>
var pageWidth = window.innerWidth
document.write('<style>html{font-size:'+pageWidth/10+'px;}</style>')
</script>
  • 在 scss 文件里添加,这个方法可实现 px 自动变 rem
1
2
3
4
5
6
7
8
9
10
11
12
@function px( $px ){
@return $px/$designWidth*10 + rem;
}
$designWidth : 750;
.child{
width: px(375);
height: px(160);
margin: px(40) px(40);
border: 1px solid red;
float: left;
font-size: 1.2em;
}

jQuery的重要性不必多讲,对于刚接触js,特别是刚学习完DOM模型的人需要了解的是为什么要学习jQuery呢,jQuery的本质是什么,它与DOM模型的关系是什么?
自己动手封装一个jQuery构造函数可以解决这些问题。

有些时候,需要我们用DOM的API封装一些函数

举一个最简单的例子,我们现在想封装这样一个函数,可以接受一个节点或者一组节点,或者一个选择器作为参数,并且给它加上一个class。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function addClass(nodesOrSelector,className){
if (typeof nodesOrSelector === "string") {
//如果参数是字符串的话,代表是一个选择器
var nodes = document.querySelectorAll(nodesOrSelector)
for (let i = 0; i < nodesOrSelector.length; i++) {
nodes[i].classList.add(className)
}
} else if (nodesOrSelector instanceof Node) {
//如果参数是一个node节点
nodesOrSelector.classList.add(className)
} else if (nodesOrSelector instanceof NodeList || nodesOrSelector instanceof HTMLAllCollection) {
//如果参数是一个节点集合,nodeList或HTMLCollection
for (let i = 0; i < nodesOrSelector.length; i++) {
nodesOrSelector[i].classList.add(className)
}
}
}

放到命名空间中

封装完之后我们决定优化一下,这是我们创造的工具呀,放在全局作用域里面可不好,为什么不放到我们的命名空间里面呢

1
2
window.qikke = {}
qikke.addClass = function(){...}

放到Node的prototype中

放到命名空间之后发现调用的时候有一点奇怪

1
qikke.addClass('ul > li','red')

我们想要的效果是这样的

1
xxx.addClass('red')

所以我们大胆的吧addClass这个函数放到了Node.prototype中

1
Node.prototype.addClass = function(){...}

于是一个接一个的工具函数被放到了Node的原型中。。。时不时的还会被覆盖掉几个。。。

写一个jQuery构造函数

所以我们下决心要自己写一个构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
window.jQuery = function (nodesOrSelector) {
var nodes = {}
if (typeof nodesOrSelector === "string") {
//如果参数是字符串的话,代表是一个选择器
//querySelectorAll返回一个nodeList 会从nodeList继承很多方法,过滤掉。
var temp = document.querySelectorAll(nodesOrSelector)
for (let i = 0; i < temp.length; i++) {
nodes[i] = temp[i]
}
nodes['length'] = temp.length
} else if (nodesOrSelector instanceof Node) {
//参数是一个node节点,把它构造成一个伪数组
nodes = {
0: nodesOrSelector,
length: 1
}
} else if (nodesOrSelector instanceof NodeList || nodesOrSelector instanceof HTMLAllCollection) {
//参数是一个节点集合,nodeList或HTMLCollection
for (let i = 0; i < nodesOrSelector.length; i++) {
nodes[i] = nodesOrSelector[i]
}
nodes['length'] = nodesOrSelector.length
}

//给nodes对象增加addClass方法
nodes.addClass = function(className){
//为nodes中的节点增加一个class
for(let i = 0; i < nodes.length; i++){
nodes[i].classList.add(className)
}
}

//返回构造好的jQuery对象
return nodes
}

var $div = jQuery('li')
$div.addClass('red')

总结

  • jQuery是一个构造函数,它可以包装普通的DOM对象,然后获得jQuery.prototype中的方法。
  • 而包装后的对象$div失去了与Node对象的联系,所以不能调用Node中的方法了。

以下纯属个人的理解和思考,肯定会有理解有误的地方,如果有新的理解,我会及时修改,也希望大家能多多指正。

原型链存在的意义

构造函数生成实例对象有个缺点:无法共享属性和方法,所以为了解决这个问题,js在构造函数中设置一个prototype属性,把需要实例对象共用的属性和方法放进去。

一张关于原型链的图片

222.png

理解这幅图的前提:

  • Function、Object、Number、String 看似是数据类型,其实是构造函数。
  • Foo是通过Function创造出来的一个函数。
  • num是通过Number构造的一个数值。
  • [[prototype]]是指向构造函数的原型对象的指针,可以通过proto属性访问。
  • Prototype是原型对象,只有函数才有prototype属性。
  • 实例的[[prototype]]指向构造函数的prototype。
  • 子类的prototype是父类的实例。

思考的点:

  • 所有函数(除了Object)的prototype都指向Object的prototype,也就是说所有函数都继承自Object(包括Function)。
  • 所有函数(包括Object)都是Function的实例,体现在[[prototype]]指向Function.prototype
  • 所以有意思的点是:Function继承自Object,而Object是Function的一个实例。

总结:对象和函数的关系

  • 函数与对象之间,绝对不是一种包含和被包含的关系。
  • “函数是对象”,这句话中的对象,绝对不是一个普通的对象。

  • 所有函数继承自Object,可以认为函数是 Object 的“子类”。(这里的Object也可以读作“对象”,但是它不是我们经常使用的那种普通对象,它是一个构造函数)。

  • 而我们平时用到的那些普通对象,是Object的实例。
  • 所以函数可以跟Object扯上关系,当最好不要跟普通对象扯上关系。
  • 函数的prototype是一个普通对象。

一直以来认为博客对我的意义不大,因为我比较喜欢做笔记,复习笔记,重构笔记这一套学习方法,再者也感觉写博客麻烦,一直无法克服自己的懒惰心理。
但是笔记始终是感觉是给自己看得,知识点非常的集中并且抽象,这导致自己的学习思维打不开,很封闭。而写博客能改变这种现象,想明白这个问题后,那肯定要写博客了,在进步的道路上不能有懒惰存在。

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment