0%

为什么要使用JSONP?

在早期,访问html页面必须进行跳转,页面的展示也依靠点击超链接,由于加载一个新页面,这不可避免的需要进行刷新。
有些利用页面种嵌入iframe,点击链接/按钮iframe访问页面,达到页面不刷新的目的,这种实现方式很丑陋。

而JSONP是一种更方便的更新数据方式,类似于现在的ajax

JSONP具体是怎么做的?

  1. 创建一个随机的函数名,
  2. 创建一个<script>,并将src指向要访问的后端服务器地址,如/?callback=函数名
  3. <script>加入文档就会立即向指定地址发起请求
  4. 服务器收到请求,将数据写入自己要返回的内容中,如函数名(参数)
  5. 客户端收到,立即执行了<script>中的内容,也就是函数名(参数),通过这种方式就完成了动态的更新。

JSONP参数可以是任意的,不过为了统一,一般使用callback=这种调用。

JSONP的请求是跨域的,后端服务器只要实现了callback调用,就可以直接使用。但同时由于使用的是动态加载<script>资源,JSONP只能使用get方式请求资源。

jQuery对其进行了封装,调用方式如下

1
2
3
4
5
6
7
$.ajax({
url: "xxxx"
dateType: 'jsonp'
success: function(A){
...
}
})

我的简单实现可以在这里

DOM标准

标准前的标准

在未指定标准前就已经有了事实上的标准
如:

1
2
3
button.onclick = function{

}

DOM1

有了更多的事件

  • blur
  • click
  • focus
  • select

DOM2

事件被单独列出来,有了拥有了事件流,事件捕获,事件冒泡,事件取消,也是最广泛的版本

事件详解

声明方式

  1. 内联声明
    1
    2
    3
    4
    5
    6
    7
    <script>
    function xxx(){
    }
    </script>
    <button onclick="xxx"></button>
    <button onclick="xxx()"></button>
    <button onclick="xxx.call"></button>

这种方式比较奇怪的时第一种方式无效,下面两种方式有效,实际上这里等同于eval(xxx())

  1. js声明

    1
    2
    3
    4
    function xxx(){
    }
    button.onclick = xxx
    button.addListener('click',xxx,options)
  2. onclickaddListener的区别
    前者是一个属性,只能赋值一个,后者是一个队列,可以监听多个事件,并按照声明顺序触发。
    除此之外,第三个参数还可以指定一些布尔值

事件模型

捕获与冒泡

在DON的事件模型中,事件传播被分为两个阶段,首先某事件触发后,会把该事件从父标签一级级传递给最终的子标签,这个阶段叫做捕获阶段,最终的子标签得到这个事件后,再把这个事件一级级返回到最上层父标签,这个过程叫做冒泡阶段,默认声明的事件是在冒泡阶段触发。值得注意的是,最终的子标签并不区分冒泡或捕获阶段,声明的多个事件按照声明先后触发。

如果只指定一个布尔值,则指是否在捕获阶段捕获事件

1
button.addListener('click',xxx,ture)

默认为在冒泡阶段捕获,也就是此值为false

阻止传播:

1
2
3
function (e){
e.stopPropagation()
}

另外jquery()的阻止默认事件是直接传false

1
$(xxx).on('click',false)

应用

怎么达到点击别处关闭浮层的效果?

document的冒泡阶段监听click事件,如果监听到就关闭浮层,而在浮层上监听click事件,阻止click事件冒泡。

我们还可以不使用阻止冒泡,我们在浮层被点击后添加一个0定时器,监听document,定时器会在空闲时触发,也就是当前事件传播结束,OK。

实现轮播细节
把第一张和最后一张复制到前面和后面,这里用clone(true)进行深克隆
复制后还要让其显示第二张

hidden()show()就可以隐藏动画,但是短时间的操作浏览器会合并,offset()可以让css立即生效而不计算

jquery还可以监听所有子元素的,为其添加事件

1
$('xxx').on('click','button',function())

这里 第二个参数是选择器

当我们使用下面的div的id是会发现得到的并不是我们想要的

1
<div id="parent"><div>

1
console.log(parent) //window

全局变量很容易造成变量名冲突,应该尽量少的使用全局变量

构建局部作用域

既然不能创建全局变量,我们就应该创建一个局部作用域
在使用let的情况下,一对{}就可以包含一个局部作用域,但在使用var的情况下,我们需要使用函数创建局部作用域。

1
2
3
function (){
...
}.call()

在一些浏览器中会遇到语法错误,我们可以使用()包裹整个函数,也可以在前面加上-或者+,或者!,只要是单目运算符都可以

要达到的效果

我需要这个小demo可以在接受Node的实例和选择器,并且可以立即调用相关的操作方法

侵入式或者非侵入式?

我其实可以直接在Node的原型对象上加上我自己写的方法,其它什么都不用做,不过这种方式会霸占变量名,而且更改了原来的原型对象,很容易带来冲突 ,所以可以选择自己新增一个方法,从这个方法里调用原来Node的方法,不对原来的Node做任何变动,这种方式叫就非侵入式。

具体实现

这里其实有两种实现方法,一种是直接用函数构造出具有相关方法属性的对象,一次搞定。

1
2
3
4
5
6
7
8
9
function xxx(){
let x ={}
...
//找到的Node实例加到x种
x.a =function(){
...
}
return x
}

另一种是构造出相关属性的对象,再构造一个拥有相关方法的原型,将proto指向原型

1
2
3
4
5
6
7
8
9
10
11
12
let yyy ={
x:function (){
...
}
}
function xxx(){
let x ={}
...
//找到的Node实例加到x种
x.__proto__=yyy
return x
}

后面这种方式会更好一点,不过先使用前一种种方式实现一次

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
window.jQuery = function (nodeOrSelection) {
let elements = {}
if (typeof nodeOrSelection === 'string') {
let temp = document.querySelectorAll(nodeOrSelection)
for (let i = 0; i < temp.length; i++) {
elements[i] = temp[i]
}
elements.length = temp.length
} else if (nodeOrSelection instanceof Node) {
elements[0] = nodOrSelection
elements.length = 1
}
elements.addClass = function (nodeClass) {
for (let i = 0; i < elements.length; i++) {
elements[i].classList.add(nodeClass)
}
}
elements.setText = function (text) {
for (let i = 0; i < elements.length; i++) {
elements[i].textContent = text
}
}
return elements
}
window.$ = jQuery

$('li').addClass('red')
$('li').setText('hi')

这里还可以写个自己的forEach,这样不用每次都自己写遍历了。
OK.

什么是DOM?

DOM全称为Document Object Model,是js用来操控XHTML,在DOM中,所有标签都是node的子集,文档最开始的属性标识了文档采用的版本,比如<!DOCTYPE html>表示是html5,这个属性每个HTML/XHTML文档都必须具有。将文档每个标签以树形的方式组织方便js对 其进行操控,着建里起的模型就叫DOM。

node

DOM中所有标签及元素等都被抽象地称为node节点,比如对于标签元素,还具有

DOM标准

Element对象代表页面中具有的标签,构造函数为Element,整个文档被称为document,对应的构造函数为Document
元素中的文字被Text构造出来

Element、Document、Text都继承自node,其它结点还有注释结点Comment这些,不过平时很少用

nodeType

编号 节点
1 标签
3 文字
7 注释
9 document
10 声明文档类型的标签
11 虚拟DOM

属性

nodeName

返回节点的名称,除特例外总是返回名称的大写
特例:

  • document
    直接使用document.nodeName返回的是#document,需要使用document.documentElement.nodeName,才会正常返回HTML

  • svg
    svg很特殊,只有它的标签是小写

innerText

获取到其节点内的所有文本内容(微软发明的)

textContent

和上面一样,只是不会忽略style和script标签,性能会更好一点。(火狐发明的)

方法

appendChild()

添加儿子

cloneNode()

默认进行浅拷贝。如果传true进行深拷贝。

contains()

是否包含指定元素

hasChildNodes()

是否有子节点

insertBefore()

插入人到元素前面

isEqualNode()

元素类型、内容、属性完全一致

isSanmeNode()

同一元素

removeChild()

从DOM树中移除,等待被垃圾回收

replaceChild(newChild,oldChild)

替换成指定的子节点

normalize()

常规化,在一个”规范化”后的DOM树中,不存在一个空的文本节点,或者两个相邻的文本节点。

Document接口

属性

body

包含当前页面的元素

characterSet

返回使用的字符编码

childElementCount

返回子元素个数

domain

返回域名

location

一个包含地址信息的对象

readyState

是否正在下载

referrer

从哪里访问过来

visibilityState

该页面是否被显示

children

doctype

documentElement

fullscreen

hidden

images

onxxxxxxxxx

origin

plugins

readyState

referrer

scripts

scrollingElement

styleSheets

title

方法

close()

页面加载完毕后自动关闭

write()

向文档内写入东西,如在open时调用write会向文档追加内容,如果已经关闭了调用write会重写documentn的内容,这十分危险! 不能在任何异步或者延时性的操作上使用write

writeln()

写一行

open()

页面一开始打开的时候启用open,可以向文档内写入数据

execCommand()

文本编辑器会用到这个

createDocumentFragment()

createElement()

createTextNode()

exitFullscreen()

getElementById()

getElementsByClassName()

getElementsByName()

getElementsByTagName()

getSelection()

hasFocus()

querySelector()

返回找到的第一个元素

querySelectorAll()

以伪数组的形式返回符合的所有元素

Element

属性

innerHTML

其中的HTML,被改写有安全风险

一些其它需要注意的点

看下面的代码

1
2
3
4
var parent = document.getElementById('parent');
parent.childNodes.length // 2
parent.appendChild(document.createElement('div'));
parent.childNodes.length // 3

1
2
3
4
var allDiv = document.querySelectorAll('div')
allDiv.length // 假设是 2
document.body.appendChild( document.createElement('div') )
allDiv.length // 还是2

这里length没有改变时因为parent.childNodes是动态集合。所谓动态集合就是一个活的集合,DOM树删除或新增一个相关节点,都会立刻反映在NodeList接口之中。
document.querySelectorAll方法返回的是一个静态集合(伪数组)。DOM内部的变化,并不会实时反映在该方法的返回结果之中。

函数是什么?

函数在js中是以字符串的形式储存起来的(这会带来效率问题吧?)

在js中函数也是个对象,执行的代码块以字符串的形式保存在对象某个地方,使用f()就是调用这块代码。
传统的调用方式是使用.call()执行函数。

声明方式

具名函数

就是有名字的函数

1
2
function f(){
}

具名函数的声明,初始化,赋值都会被变量提升至顶部

匿名函数

就是没有名字的函数

1
2
var a = function (){
}

1
2
3
4
a() //报错
var a = function (){
console.log(666)
}

用此种方式,是以变量提升的方式,声明,初始化被提升至顶部,在声明前调用会错误。

具名函数赋值

1
2
3
var a = function b(){
console.log(666)
}

这种方式其实等于匿名函数,b不能被使用,但能在函数内部使用。(BUG js)

函数对象

1
2
3
4
5
6
new Function('x','y','return x+y')

//或者
var n =1
var z = new Function('x','y','return x+'+n+'+y')
z(1,2) //返回4

箭头函数

1
(x,y) => x+y

这回直接返回 x+y,如果参数只有一个,可以省略前面的括号,后面只有一个个表达式可以省略后面的大括号,箭头函数总是匿名函数

相关函数

f.name

得到函数名
匿名函数会得到指向它的变量,具名函数赋值会得到具名函数的值……
如果使用Function()声明,总是返回anonymlus,表示匿名的

f.call()

f.call()就是执行函数体
第一个参数为执行的对象,其余为函数的参数,这是js真正的调用方法,f()可以说只是一种语法糖

call()的第一个参数可以用this得到,后面的参数可以用arguments得到

this

在普通模式,如果传入的是null或者undefined,则默认为Window
,如果开启严格模式,则不会自动转变,传入什么就是什么,严格模式可以使用use strict开启。

严格模式中,参数的类型转换不会发生,比如这样

1
2
3
4
5
function f(){
'use strict'
console.log(this)
}
f.call(1)

本来1不是对象,但call需要第一个是对象,就会把1自动装箱成Number对象,使用’use strict’会禁用参数的自动装箱

arguments

arguments是一个伪数组,虽然是数组的形式,但proto指向Object,不指向Array。
它储存着函数调用时所有的实参。

函数调用过程

函数调用过程是以栈的形式进行调用,先进后出,在我们递归调用时,尤其要注意栈空间的占用,否则可能溢出(stack Overflowd网站的名字,233),如果递归时不会使用函数内的变量,会进行尾递归优化,不再存储函数内的变量,减少栈空间调用。

作用域(scope)

函数内会构成一个新的作用域,多个函数的嵌套,构成了一棵作用域树

这里我们就需要区别几种声明变量的方式

  • 不加关键字声明
    因为作用域是一棵树,找不到会向上找,直到找到window对象,如果window对象也不具有该值,就认为是为window设置键值对,这就创建了全局变量
  • var 声明
    var 声明会被提升至该作用域顶部,并在顶部初始化为undefined
  • let
    let声明同样会被提升至顶部,但初始化和赋值只会在进入let所在区块进行,未在所在区块的使用都被认为是非法的

前言

js中window拥有非标准库和标准库,标准库中有Object及相关api,Sting,Boolean等,我们可以先试试这些函数/对象

Object()

Object()会根据传入的参数返回不同的值

  • 基本类型,所有可以被包装的的基本类型都会被包装成包装类型
    传入空对象,undefined,或者不传,返回null,传入非空对象返回原对象

使用new Array()方式和构造函数没有区别

这里我们可以总结一下
如果是基本类型的函数/对象,不加new和加new是不相同的
如果是对象,比如Function 我们这里的Array,这两者是等同的

Function构造函数

let a = new Function('a','b','a+b')
参数可以无到任意,但至少有函数体

function是个关键字,Function是个全局对象

函数有几种声明方式,声明出来的结果也不同
具名函数

1
2
3
function f(){
xxx
}

匿名函数

1
2
3
var f = function (){
xxx
}

函数方法体

1
new Function('a','b','a+b')

Function.prototype

.call()
.bind()
.apply()

Number() Boolean()

转换为对应的基本类型

Array是什么?

Array对象是用于构造数组的全局对象,属于Object

我们一般声明Array采用的是[gg, aga, aga],实际上这种方式等价于new Array,前者可以当作语法糖。

具体怎么用?

构造函数

Array()构造函数会根据传入的不同参数返回不同的数组

  1. let a = Array(1)
    当参数只有一个数字的情况,参数代表数组的长度,返回等于参数长度的数组,这个数组其它元素都没有被声明,包括0 in a,它返回undefined。

    请注意,传入NaN会报错

  2. let a = Array('我就要看你要做什么')
    当参数为一个非数字,返回一个长度为1且a[0]=所给字符串数组

  3. let a = Array(3,3)
    当传入参数大于等于2,按顺序填充新数组并返回

方法

string.prototype

  • .trim() 返回一个去掉两端空格的新字符串
  • .spli() 返回一个分割后的新字符串

Array.prototype

.push() 推入一个(末尾)
.pop() 弹出一个(末尾)
.shift() 删除数组头部元素
.unshift 添加到头部
.join() 把数组连接起来,并加上分隔符形成字符串返回,默认为,
.concat(b) 把数组连接起来返回新数组,也可以和空数组合并,等同于数组复制
.forEach() 遍历数组,详见后文
.map() 遍历数组,详见后文

Array的关键是继承了Array.prototype

数组遍历

如果是数组,可以使用for(let i=0;i<obj.length;i++>)
如果不关心是否是数组,可以使用for (let i in obj)遍历所有键值
还可以使用forEach().map()遍历

forEach()

forEcah接受一个函数,这个函数有是三个值

1
2
3
function (currentValue,index,array){
//当前值,索引,数组
}

.map()

和forEach差不多,不过对value的改变会以新数组的形式返回

a.filter()

和map差不过,就是筛选,ture要,false不要

.reduce()

每次会把函数的返回值作为下一次的返回值,还可以指定初始索引,最后返回最后执行后的函数返回值

1
a.reduce( function (sum,n){return sum+=n},0) //返回6,0为初始索引

a.sort()

一般排序算法使用的都是快排
注意:js中默认排序是按照Unicode的码点进行排序,非常傻逼。

幸运的是,sort接受一个可选参数,支持自定义的比较算法。
函数必须是这种形式的

1
2
3
4
5
6
7
8
9
10
function (a,b){
f (a < b ) {// 按某种排序标准进行比较, a 小于 b
return -1;//小于0 a排前面
}
if (a > b ) {
return 1;//大于0 b排前面
}
return 0;//相等不变,但实现不一
}
}

因此比较数字大小的话,可以这样

1
a.sort(function (a,b){return a-b}) // 最小的在最前

1
a.sort(function (a,b){return b-a}) // 最大的在最前

其中forEach使用this表示调用者,可以使用this来获取调用者的属性

伪数组

如果像一个数组,且_proto_不等于Araay.prototype,那这就是一个伪数组
在js目前遇到的伪数组就是arguments这个表示传给函数的参数

写本文得目的主要是为了帮助我的笨蛋马q儿子,面向0基础的小白,看完这篇文章你也就可以为自己重装系统,不花一份钱啦!

简单装机

注意,名为简单装机的原因是一以下所有的步骤都是理想状态下的,由于系统、各厂商bios不同,历史各种版本迭代原因,带来了许多兼容性问题,本应该丝滑流畅的安装过程总有可能遇到难以解决的兼容性问题,我可以尽可能的把我所遇到的问题及解决办法写下来,但不可避免会让文章变得冗长,晦涩,因此我决定不写这方面的坑,如果你遇到了,去<baidu.com>或者<google.com>上寻找解决方案吧!网络是最大百科全书。

安装前的准备

你需要的硬件准备

  • 一个至少8G的U盘
  • 一台能上网的PC

你需要的软件准备

下载工具

你可以选用迅雷,尽管它很臃肿。

下载系统

请注意,我希望你仅仅只在<msdn.itellyou.cn>或者微软的官方渠道下载你需要的系统,在其它地方下载系统存在一定的风险。

  • 第一步
  • 第二步
    我总是建议你使用当前最高版本的win10
  • 第三步
    内存高于4G的总是选用x64(64位的)
  • 第四步
    复制这个框内的链接
    一般情况下迅雷检测到会自动弹出,如果没有你可以自己打开迅雷新建任务,它会自动识别剪切板的内容,创建下载任务。
    选择你想储存到的地方,开始下载。

下载安装镜像写入工具

我使用的(UltraISO软碟通 ),你可以去这里下载https://cn.ezbsystems.com/download.htm

下载后直接安装即可

开始制作系统安装U盘

  • 第一步
    打开UltraISO

    点击试用,对我们来说试用版的功能已经足够了
  • 第二步
    找到你下载的系统镜像

    双击它

    之后你会看见这样的目录及结构,这表示你已经进入镜像文件里了
  • 第三步
    插入U盘
    单击上方的启动按钮

    选择写入硬盘镜像

    这时就会弹出设置,设置如何把系统镜像写入U盘的方式
    写入方式我建议使用USB-HDD的方式

    我建议你在写入前格式化你的U盘,这回带来更好的兼容性
    请注意备份你的U盘数据
    然后点击写入,就开始将系统镜像写入到你的U盘,整个过程大约在5-10分钟
    另外请记住你的U盘物理名,这在后面有用。

    恭喜你!你已经制作好了系统安装U盘

安装系统

安装系统是整个环节最困难最容易出问题的环节,我们得离开我们熟悉的windows进入bios,并且不同品牌的biso往往不尽相同。

选择从我们的U盘启动系统安装

简单方法

方法: 直接插入U盘,开机,就可以直接进入系统安装界面

是的!简单方法我们仅仅只需要插入U盘就行了!一般来说近几年的电脑都支持这种方法,是不是很便利!

复杂方法

请注意!当你无法使用上面的方法进入系统安装界面,通常意味着你的bios很老旧或者厂商对你的bios做了一些奇奇怪怪的修改,你可能会遇见大量的兼容性问题,你可以尝试百度/谷歌相关错误现象/提示来解决

  • 复杂方法一
    搜索你的电脑品牌+快捷启动,或启动选项,这些教程会很详细的告诉你如何进入快捷启动。

进入快速启动后,选择你的U盘,enter确定就可以进入安装界面了

  • 复杂方法二
    同样搜索你的电脑品牌+设置启动顺序,你也可以看见大量教程了,按照他们说的做,不要乱设置,设置完重启就可以了。

正式安装系统

如果你成功的从U盘启动了系统安装,你会看见这样的界面

点击下一步,再点击现在安装,你会过几秒后看到这个页面

请记住总是选择专业版

点击下一步我接受许可条款下一步

选择下方的自定义安装,升级选项总容易出问题

选择后你会看到这样的界面

如果你是全新的电脑,这里只会出现未分配的空间,重装的就会出现你的硬盘分区和默认隐藏的分区。

全新的电脑你可以自由分配空间,重装的电脑你选择C盘下一步就可以了。

完成后记得一定要拔掉U盘,否则又会进入安装界面。

激活

使用微软账号激活

如果你之前有一台已经激活的win10,并注册了微软账号并在激活的win10上登陆过,在新的win10系统上你登陆你的微软账号就可以自动激活了,激活有数量限制,具体我未测试过。

使用产品密钥激活

你可以直接在淘宝上买到激活码,搜索win10专业版密钥,大概10元一个,激活后你可以注册微软账号,以后都可以激活自己的系统了。

使用kms破解激活

你可以直接使用关键字win10破解搜索到相关工具,而且基本一定能破解,但是这个激活只能维持180天,kms会在180天后自动重新激活,并不方便,而且下载破解软件的过程中,可能感染病毒、被安装流氓软件,这是最不推荐的方式。

ok,一切完工啦!

全局对象window

标准里名为 global
但浏览器实现为window,global具有的window都有

ECMAScript规定的

如:

  • parseInt()
  • parseFloat()
  • ……

    浏览器私有

    每个浏览器不一样,
    比如chrome/firefox具有
  • alert()
    弹出提示框
    还有
  • prompt()
    提示输入
  • confirm()
    提示确认
  • console()
    控制台打印
  • setTimeout(function,3000)
    指定毫秒后执行函数
  • document
    document也是私有的
    dom规范由w3c指定

声明对象

var n = new number(1)
把1包装成对象,会有更多的一些方法

但是基本类型其实也可以使用各种方法,这是因为JS会在发现把其当作对象时,自动以它的值创建对象,调用结束就把它删除,这就像java的自动装箱/拆箱,所有的基本类型都是如此,包括string

语法糖

1
2
a = {}
var = new Object()

这两者功用是一样的

String对象

  • .toString(a) a指定以几进制显示
  • .charAt()返回指定索引的字符
  • .charCodeAt(0) 返回指定索引的unicode编码
  • .trim 去掉多余的空格
  • .concat(s) 将当前字符串与参数里面的字符串连接起来并返回(那和+有什么区别?)
  • .slice(1,2) 返回指定索引到指定索引的字符(0,1]

Boolean对象

注意:new Boolean() 也是true

原型链(继承?)

为了解决什么问题?

某一类对象拥有共同的方法,或属性,每个对象都声明一次既费时间又浪费内存,这时候使用继承就OK啦!

怎么解决的

拥有共通属性的对象,把共通的属性抽象出来作为一个父对象,子对象不再存储这些共通的属性和方法,而使用proto存储父对象。

当我像调用toString()时,过程是怎样的?

过程:

  1. 判断是否是对象,如果是,自动装箱将其包装成对应的临时包装对象。
  2. 查找当前对象是否包含toString()方法。
  3. 如果当前对象不包括,访问__proto__里存储的父对象,继续寻找toString()
  4. 如果找到停止寻找,否则继续递归下去。

从这里就可以看出,如果子对象和父对象拥有同样的方法/或者属性,总是使用的子对象的值,我们可以把这个称作覆盖

Object是所有对象的父对象,Object再向上就为null

如何设置公有属性?

这里我们其实需要理解是如何创造对象的?

1
var n = new Object()

在这里 其实new Object()实际在调用一个函数,我们可以称作这个函数为构造函数,函数返回一个根据参数生成的对象。

因此这里可以看做

1
2
3
4
var n = function Constructor(){
...
return object
}

实质上,在js中,函数也只是一种对象

Function对象

如果我们使用console.dir打印Obeject、Number、Function的所有属性,我们就能真正理解proto和prototype具体是个什么鬼

  • Function

    1
    2
    3
    ...
    prototype : f()
    __proto__ : f()
    • 展开prototype
      1
      2
      ...
      prototype : Object
  • Object

    1
    2
    prototype : 一堆我看不懂的东西
    __proto__ : f()
  • Number

    1
    2
    prototype : 一堆我看不懂的东西
    __proto__ : f()

我们发现了什么?
我们知道使用new 关键字创建的对象会得到原型对象的prototype的地址并保存在__proto__中,这样就能共用方法及属性。

但是,原型对象又怎么产生?
这里就解释了一切,Function是所有原型对象的爸爸,所有的原型对象都是Function构造的,而Function爸爸需要让所有原型对象最后都继承自Object,这样才能面向对象嘛,所以prototype中的__proto__ : Object表示继承自Object,这种处理方式可能就叫中间层?

至于__proto__指向自己,我猜测可能是在某些情况需要new Function来创造一个Function子对象,这样就能让Function表里如一。

以上很多都是自己的猜测,可能有许多谬误,以后加深理解了再来更新,不说了,图还没画完呢…赶作业去了

历史

1991年 李爵士发明万维网
1992年 其同事发明html和css
1993年 成立w3c
1995年 网景公司成立 能够执行脚本

js之父Brendan Eich接到任务发明一种名为Mocha(咖啡配抹茶)的脚本语言,它需要看起来像java。js发布后,Unicode发布UTF-8,这就导致js不能完整兼容UTF-81996年微软模仿JS发明了Jscript。
在IE5.5微软推出JS发请求。
2004年Gmail利用这个功能做了一个网页上的程序,这让JS的地位大大提高。

这时就出现了前端(以js为生)

制约JS发展的问题

  • 大量全局变量
  • 缺少标准库

出现了ECMAScript 5,Es4胎死腹中
Es5做了个小升级,具体谷歌

rails社区(主要使用ruby)发明了coffeeScript,非常好用,ES6就迫在眉睫。

JS集大家之所长。原创之处不优秀,优秀之处非原创

兼容性

IE8部分兼容ES5,IE7及以下则不兼容

更新

ES7及以后,每年一更。

ESnext 还未正式公布的特性,可以使用webpack打包,使其支持。

js数据类型

7 种数据类型

  1. number
  2. string
  3. boolean
  4. symbol(ES6新增)
  5. null
  6. undefined
  7. object

number

支持:

  • 十进制 1
  • 浮点数 .1 //0.1
  • 科学计算法 1.23E2
  • 二进制 0b011
  • 八进制 0214542 //以0开头并且没有大于7的数
  • 十六进制 0x5116D

string

‘你好’ “你好”

转义

为了避免冲突使用

  • \t 制表符
  • \n 回车
  • \ \

多行字符串 \就可以了,但是如果后面接字符串,就会出现错误,且不容易发现。
也可以用 +号,+号最好,这样易于阅读的

ES6新增加了字符串语法

1
2
var a =`123456
7890`

boolean

  • ture
  • false

&&

全为真才为真,因为自动类型转换的缘故,实质上可以通过这种方式来简写if

1
2
3
4
5
6
var a = ture
var b = 'z'
a&&b //返回b的值

a = false
a&&b //返回a的值

在这里,a为ture的情况下,b的布尔值就等同整个函数的布尔值,所以返回b和返回运算后的布尔值是没有区别的,a为false的情况下同理

注意,这种方式的if会难以阅读

||

一个为真就为真
简写if的形式和上面相反

symbol

有点类似java的enum

null

指针不指向任何地址,也就是地址为0。

undefined

变量初始化会被初始化为undefined,不使用null是因为设计时认为null是数字0,这可能导致误解。

Object

除Object外都为基本类型,Object以hash表的方式组织对象,就是基本类型的组合。
注意[]表示声明一个空数组,typeof [] 返回Object,但是以{0:’a’,1:’b’},也是可以以数组的形式访问,但是instanceof Array返回false

声明方式

只支持以字符串声明key,注意最后以,结尾是ES5的新特性,ES3不支持,IE7及一下只支持IE

1
2
3
4
var ob = {
name: 'aarG',
age: 18,
}

空字符也可以是key,调用使用[‘’]
key不加引号视为标识符,不可以以数字开头
同时,如果key符合标识符,可以使用.来更方便的取得

更改方式

  • in 判断key是不是在对象当中,js声明的全局变量实际都绑定在window上,可以用in判断是否声明过
  • delete 删除一个键值对
  • for(var key in person) 遍历键值

遍历方式

1
2
3
for (var key in Object){
console.log(key )
}
1
Object.keys()

得到一个可枚属性组成的数组

typeof

返回对象的类型和方法

注意 typeof null返回Object

去TM的js类型转换

xx.toString()

  • number 以字符串的形式输出,可选参数为以何种进制输出数字
    注意:以1.toString()的方式是不可行的
  • boolean ture或者false
  • null 空指针,直接报错
  • undefined 直接报错
  • Object 总是输出[object Object],除非重写覆盖了toString()方法
  • {} 语法不支持

小技巧: xx + “”
js发现+运算符,会检测算子是否有运算符,有就转为字符串

  • 1 + ‘1’ “1”
  • ture + ‘’ “true”
  • {} + ‘’ 0 //不懂,可能是对象转为0,空字符串再转为0

window.String()

支持null undefined {},其它和toString()一致

window.Boolean()

7种类型种,只有以下几种是false

  • number NAN 0
  • null
  • undefined
  • Object 全为true
  • String ‘’

快速转boolean !!

window.Number()

  • boolean 0或1
  • null 0
  • undefined NaN
  • Object 调用valueOf,根据返回的值做决定,如果返回对象则为NaN
  • string 如果为空字符串返回0,如果为数字则一个个解析,有非数字就返回NaN,其它返回NaN,特殊的是参数为011会被认为是11

window.paparseInt()

  • 参数为字符串,会从头开始寻找数字,直到遇到非数字为止,没有找到返回NaN
  • 如果开头为空格,会省略空格
  • 空字符返回NaN
  • 所有其它类型返回NaN

window.parseFloat()

和paparseInt()一致,只是不会取整

小技巧

  • ‘1’ - 0
  • +’1’
  • -()
    都可以

内存模型

内存具有内存颗粒,一般每个内存颗粒有512Mb,多个内存颗粒组成内存容量

内存分配

浏览器(chrome)启动后会占用大量内存,并且每个新开页面都独立分配内存,这些内存又会分配给HTML+css,js,网络,定时器这些,一般JS内存只能申请到100Mb

JS种内存分为两个区,一个区分为数据区,一个区分为代码区,我们这里只考虑数据区。

数据区又分为两个部分,一个为stack(栈),一个为heap(堆)。
js第一步先看声明了哪些变量,进行提升,这样方便内存分配
基本类型存在stack中,而Object由于数据量大,经常添加更改属性,不方便内存分配,所以Object就新开个部分存对象,声明对象后就把内存地址给变量,用指针来找对象,堆里新增加A数据是用引用的方式存储的,也就可以方便的添加删除了。

数字64位,字符16位(2个字节)

测试一下

1
2
3
4
5
6
var a = {n:1}
var b = a
a.x = a = {n:2}

a.x //undefined
b.x //[Object Object]

这里的关键是a.x这里,解释器确定变量是从左到右,先确定赋给谁,再进入表达式,所以a.x还是之前的a

垃圾回收

如果一个对象没有被引用,就会被标记可以回收,在浏览器需要的时候就会被回收。但在IE6,在多个对象一级一级引用的时候,即使父对象不存在了,子对象却不会被回收,这会导致内存泄漏,因此需要这样做

1
2
3
4
window.onunload = function{
document.onclick = null
//还要有所有的其它监听事件
}

深拷贝&浅拷贝

你变我也变就是浅拷贝
你变我不变就是深拷贝(所有的引用都被复制,和原对象及其子对象没有任何关联)