自己实现一个jQuery

What is jQuery?

核心就是一句话 The Write Less,Do More(写更少,做更多) , ok , let’s go!

  • addClass( )功能 (最渣版)
1
2
3
4
5
6
7
<ul>
<li id="item1">选项1</li>
<li id="item2">选项2</li>
<li id="item3">选项3</li>
<li id="item4">选项4</li>
<li id="item5">选项5</li>
</ul>

如何给id=”item3” 添加样式

1
2
3
4
5
6
7
8
9
10
function addClass(node,classArr){
var len = classArr.length;
for(let i = 0 ;i < len ; i++){
node.classList.add(classArr[i]);
}
}
// item3 通过id名可以直接获取元素
addClass(item3,['red']);
// 你也可以这样
addClass(document.getElementById('item4'),['red'])

注意:通过id获取元素不是100%没问题的,你要是非 id=”parent” 这样就不好使,你可以打印看看parent是啥


这样只是初步封装了方法,但是方法是还在全局作用域,你叫addClass 到时候别人也叫addClass 你可能就被覆盖了 我们是有版权的 来来来 来个帅气签名!

addClass( ) (签名版)

命名空间:给全局对象挂载一个对象 window.myDom = { }
然后在你挂载的myDom 对象上挂载你的属性 、方法

这比之前好在哪里?

好在有“从属”关系而不是“零散“的变量
(至少在阿里没落前)我是阿里的前端 相比 我是一个前端 那个更有份量呢?

1
2
3
4
5
6
7
8
window.myDom = {};
myDom.addClass = function (node,classArr){
var len = classArr.length;
for(let i = 0 ;i < len ; i++){
node.classList.add(classArr[i]);
}
}
myDom.addClass(item3,['red']);
  • 你肯定会不爽因为还不如之前用着爽了 每次还得加命名前缀
    myDom.fn(param)
    myDom.fn2(param)
    直接 item3.addClass([‘red’]) 才爽 有需求就会有改进

反正都是Node的api 我直接改原型不就好了 于是乎!你又挖了个坑

1
2
3
4
5
6
7
Node.prototype.addClass =  function (node,classArr){
var len = classArr.length;
for(let i = 0 ;i < len ; i++){
this.classList.add(classArr[i]);
}
}
item3.addClass(['red']); //你遂了心意了
  • 这样的后果是别人也这样改原型,而且也添加个addClass
    是不是心好累! 为啥?此乃优良传统
    山寨一下 改改加点“我想出”的功能 我就敢说我的比你好! (王者荣耀 与 dota )
    why?你用上了爱迪生的电灯泡 我就问你,你是自己发明一个还是在借鉴他?
    看得远是因为我站在巨人的肩膀上!

我们是讲版权的我的就是我的

我自创一个Node2行不行 以后可能还有Node3

1
2
3
4
5
6
7
8
9
10
11
12
13
window.Node2 = function(node){
return {
addClass:function(classArr){
var len = classArr.length;
for(let i = 0 ;i < len ; i++){
node.classList.add(classArr[i]);
}
}
}
}
var node2 = Node2(item4);
node2.addClass(['red']); //完美
// Node2(item5).addClass(['red']) 是不是有种似曾相识的感觉

说好的jQuery呢? 你把Node2替换成jQuery 在试试

是不是有点感觉了 这是最简单版 以后会慢慢高大上的

jQuery最强大的莫过于选择器 Selector

你肯定听说过一句话 “选择大于能力” (啊!没听过? 现在听过了!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
window.jQyery = function(nodeOrSelector){
let node ;
if(typeof nodeOrSelector ==='string'){
node = document.querySelector(nodeOrSelector);
}else{
node = nodeOrSelector;
}
return {
addClass:function(classArr){
var len = classArr.length;
for(let i = 0 ;i < len ; i++){
node.classList.add(classArr[i]);
}
}
}
}
var jqyery = jQyery(item4);
jqyery.addClass(['red']);

如果用过的人肯定知道jQuery选择器返回的是个伪数组才对

我们使用querySelectorAll()它返回的是一个节点数组(伪数组)

然后给我们内部声明的 nodes 复制获取的每个节点

如果是节点数组就挨个拷贝 如果是一个 我也把你搞成伪数组哪怕就一个甚至没有

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.jQyery = function(nodeOrSelector){
let nodes = {};
if(typeof nodeOrSelector ==='string'){
let temp = document.querySelectorAll(nodeOrSelector);
for(let i=0;i<temp.length;i++){
nodes[i] = temp[i];
}
nodes.length = temp.length;
}else if(nodeOrSelector instanceof Node){
nodes = {
0:nodeOrSelector,
length:1
}
}
nodes.addClass = function(classes){
for(let i=0;i<classes.length;i++){
for(let j=0;j<nodes.length;j++){
nodes[j].classList.add(classes[i]);
}
}
return nodes;
}
return nodes;
}
//这样我们就可以用$(选择器)调用了
window.$ = jQyery;
//$(item3).addClass(['red']);
$('ul li').addClass(['red'])

你肯定会疑惑 为啥每次都 return nodes ==> 答案就是链式操作

1
$('ul li').addClass(['red']).Text('hi'); //Text 我们还没有实现  ok 马上开始

在内部继续挂载方法 Text( ) 你传递参数就代表设置text你不传递就代表get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
nodes.Text = function (text){
if(text === undefined){
var texts = [];
for(let i=0;i<nodes.length;i++){
texts.push(nodes[i].textContent);
}
return texts;
}else{
for(let i=0;i<nodes.length;i++){
nodes[i].textContent = text;
}
return nodes;
}
}

欢迎批评指正!

END

DOM_API

DOM_API

我们写HTML时会先写一个文档头<!Doctype html> 然后html head body

1
2
3
4
5
<!Doctype html>
<html>
<head></head>
<body></body>
</html>

head/body是可以省略的,这其实就是一棵树

1

这是我们大脑中对html的理解,而不是内存中的理解

2

内存里是DOM(Document Object Model)文档对象模型

3
4

  • 页面上所有标签都有跟它对应的构造函数
  • 浏览器只要看到一个标签就会给给它构造出来一个对应的内存中的对象
  • 页面中的节点通过(构造函数)==> 对象

这就是DOM api 如果你想操作一个节点就可以操作这个对象对应的api

Node接口

1.属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
childNodes
firstChild
innerText
lastChild
nextSibling
nodeName
nodeType
nodeValue
outerText
ownerDocument
parentElement
parentNode
previousSibling
textContent
  • document // document
  • document.documentElement //html
  • document.body // 代表body

childNodes && children

如果是节点是包含回车的

1
2
document.body.childNodes //获取body下所有子节点 (注意子节点不是子元素,回车也是文本节点)
document.body.children //获取body下所有子元素

firstChild/lastChild && firstElementChild lastElementChild

1
2
3
4
5
document.body.firstChild  //body的第一个子节点  注意是子节点  回车也是文本节点
document.body.lastChild //body的最后一个子节点 注意是子节点 回车也是文本节点

document.body.firstElementChild //body的第一个子节点 注意是子节点 回车也是文本节点
document.body.lastElementChild //body的最后一个子节点 注意是子节点 回车也是文本节点

previousSibling nextSibling && previousElementSibling nextElementSibling

1
2
3
4
5
6
//兄弟节点 与 兄弟元素
document.head.previousSibling //head前一个节点
document.head.nextSibling //head后一个节点

document.head.previousElementSibling //head前一个元素
document.head.nextElementSibling //head后一个元素

nodeName

只有svg元素 返回的是小写”svg” 其他标签返回的都是大写

nodeType

  • 返回节点的节点类型 是数值(为啥是数值,因为以前的内存紧张 节省内存 8M是豪华配置 )
  • 1代表元素节点
  • 3代表文本节点
  • 除了这个 1、3它还存了一个常量 用来表示节点类型 Node.ELEMENT_NODE >

自行MDN

  • 1 元素节点
  • 3 文本节点
  • 7
  • 8 注释节点
  • 9 Document节点
  • 11 DocumentFragment 节点

innerText && textContent

在IE发明innerText之前 前端想要获取htmls代码的文本需要这样写

1
2
3
4
5
6
7
8
9
10
<div>
1<span>2</span>3<span></span>4
</div>

var text ='';
for(var i=0;i<div.childNodes.length;i++){
if(div.childNodes[i].nodeType === 3){
text += div.childNodes[i].nodeValue;
}
}

于是IE发明了 innerText 你爱用不用我自己觉得很爽

FF说了 你IE又搞特殊 于是FF出了个 textContent

innerText && textContent区别

  • textContent 会获取所有元素的内容,包括 script 和 style 元素,然而 innerText 不会。
  • innerText意识到样式,并且不会返回隐藏元素的文本,而textContent会。
  • 由于 innerText 受 CSS 样式的影响,它会触发重排(reflow),但textContent 不会。
  • 与 textContent 不同的是, 在 Internet Explorer (对于小于等于 IE11 的版本) 中对 innerText 进行修改, 不仅会移除当前元素的子节点,而且还会永久性地破坏所有后代文本节点(所以不可能再次将节点再次插入到任何其他元素或同一元素中)。

到底用谁

1
'textContent' in document.body ? document.body.textContent : document.body.innerText

2.方法(如果一个属性是函数,那么这个属性就也叫做方法;换言之,方法是函数属性)

  • createElement() //创建节点
  • appendChild()
  • cloneNode() //复制一个节点 有一个参数[param] true/false 默认false true代表深拷贝
  • contains() //Node.contains()返回的是一个布尔值,来表示传入的节点是否为该节点的后代节点。
  • hasChildNodes() //判断一个节点是否有后代节点
  • insertBefore()
  • isEqualNode() //节点是否相等 div2 = div1.cloneNode(true); div2.isEqualNode(div1) 返回true
  • isSameNode() //判断是否相同(就是是不是同一个) 你与你自己肯定是同一个人 1===1
  • removeChild() //移除一个节点,但是这个节点不是真的没了,还在内存里 只是从页面移除了
  • replaceChild() //替换一个节点,但是这个节点不是真的没了,还在内存里 只是从页面移除了
  • normalize() // 常规化
1
2
3
4
5
6
7
8
9
10
11
12
var wrapper = document.createElement("div");

wrapper.appendChild(document.createTextNode("Part 1 "));
wrapper.appendChild(document.createTextNode("Part 2 "));

// 这时(规范化之前),wrapper.childNodes.length === 2
// wrapper.childNodes[0].textContent === "Part 1 "
// wrapper.childNodes[1].textContent === "Part 2 "

wrapper.normalize();
// 现在(规范化之后), wrapper.childNodes.length === 1
// wrapper.childNodes[0].textContent === "Part 1 Part 2"

搞清楚英文单词的意思就知道用法

如果发现知道英文后依然不明白用法,看 MDN 的例子即可,如 normalize

DOM APi 无外乎「增删改查」

Document 接口

属性

  • anchors //废弃 详情mdn 返回a标签 集合
  • body
  • characterSet //返回字符集
  • childElementCount //返回元素个数
  • children //返回子元素 伪数组
  • doctype //返回文档头
  • documentElement //返回页面根元素 html
  • domain //返回域名
  • fullscreen
  • head
  • hidden
  • images
  • links
  • location
  • onxxxxxxxxx //监听事件
  • origin
  • plugins //安装的插件 静态集合
  • readyState
  • referrer //你访问一个网址 浏览器会问你的引荐者是谁 不然就会被拒之门外
  • scripts
  • scrollingElement
  • styleSheets
  • title
  • visibilityState

方法:

  • close()
  • createDocumentFragment()
  • createElement()
  • createTextNode()
  • execCommand()
  • exitFullscreen()
  • getElementById()
  • getElementsByClassName()
  • getElementsByName()
  • getElementsByTagName()
  • getSelection()
  • hasFocus()
  • open()
  • querySelector()
  • querySelectorAll()
  • registerElement()
  • write()
  • writeln()

考点

  1. previousSibling nextSibling
  2. innerText 和 textContent 的区别
  3. nodeType 1代表元素节点 3代表文本节点
  4. cloneNode() 设置参数可以深拷贝
  5. isEqualNode isSameNode 的区别 isEqualNode是两个节点是否一个样 isSameNode 是 是否是同一个
  6. normalize() 主要体现mdn的作用

JS里的Function

函数的基本结构

1
2
3
4
function fn(参数1,参数2){
//不写return 它会自动返回 undefined
return undefined
}

函数的五种声明方式

1.第一种 具名函数

1
2
3
function x(a,b){
return a+b;
}

2.第二种 声明一个匿名函数 但是不能单独使用,会报错
需要赋值给一个变量

1
2
// function (a,b){return a+b} 报错
var fn = function(a,b){return a+b}

3.第三种 具名的函数赋值给一个变量

1
2
3
4
5
6
7
var x = function y(a,b){return a+b;}
//console.log(y) 报错 y没有定义
//但是单独定义一个函数 function x(){}
//然后console.log(x) 不会报错
//js又一垃圾之处 不一致性 语法没有错误 原因在于作用域
// function x(){} 全局作用域
// var x = function y(){ //作用域在函数里面}

4.第四种 window.Function 全局对象

1
2
3
4
5
var f = new Function('a','b','return a+b');

//面试题 假定n=1
var f = new Function('a','b','return a+'+n+'b')
f(1,2) //4

5.第五种 箭头函数

1
2
3
4
var f = (x,y)=>{return x+y}
var f2 = (x,y) =>x+y;
var f3 = x=>x*2;
var f4 = (x,y)=>{var a = 1; var b =2; return a+b+x+y;}

函数的name属性

1
2
3
4
5
6
7
8
function f(){}
f.name // "f"
var f2 = function(){}
f2.name // "f2"
var f3 =function f4(){}
f3.name // "f4"
f5 = new Function('x','y','return x+y');
f5.name //"anonymous"

函数的本质

1
2
3
function sum(x,y){
return x+y;
}

sum(1,2) <==> sum.call(undefined,1,2)

小白都这样用 sum(1,2) sum(2,5) 这个是语法糖 但是对你深入没有任何帮助
硬核玩家这样用 sum.call(undefined,1,2)

只有sum.call()调用的方式 你才能真正理解 this

this && arguments

  • f.call(undefined,1,2) 的第一个参数是 this
  • f.call(undefined,1,2) 第一个后面的参数是 arguments

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    var f = function(){
    console.log(this)
    }
    f.call(1) //1
    f.call('sss') //'sss'


    // 潜规则 普通模式下 如果this是undefined 浏览器会自动把this变成window
    f.call(undefined) // window

    //如果是严格模式 this就是原本的值
    var f2 = function(){
    'use strict'
    console.log(this)
    }
    f2.call(undefined); // undefined
arguments
1
2
3
4
5
6
7
8
9
10
11
12
13
var f = function(){
console.log(arguments);
}
f.call(undefined,1,2,3)
/*
{
0:1,
1:2,
2:3,
length:3
callee:f(){}
}
*/

arguments 不是数组 它的原型链中没有Array.prototype

call stack 调用栈

1
2


作用域面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = 1;
function f1(){
var a = 2;
f2.call();
console.log(a);

function f2(){
var a =3;
console.log(a);
}
}
f1.call();
console.log(a);
// 3
// 2
// 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a = 1;
function f1(){
f2.call();
console.log(a);
var a= 2 ; //变量提升

function f2(){
var a =3;
console.log(a);
}
}
f1.call();
console.log(a);
// 3
// undefined
// 1

注意分析时要把所有变量提升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var a = 1;
function f1(){
console.log(a);
var a= 2 ; //变量提升
f4.call();
}

function f4(){
console.log(a);
}
f1.call();
console.log(a);
// undefined
// 1
// 1

闭包

1
2
3
4
5
var a  = 1;
function f4(){
console.log(a);
}
//如果一个函数,使用了它范围外的变量,那么这个就叫闭包

JS里的Array

基本类型 && 复杂类型

复杂类型的对象使用不使用new都会返回对象

1
2
3
4
5
Objcet(1)  // 返回一个Number对象
Object('sss') //返回一个String对象
Object(true) //返回一个 Boolean对象
Object() // 返回一个对象 {}
new Object() //返回一个对象{}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
String(1) //  '1'
String(true) // 'true'
String(null) // 'null'

new String(1) // String对象
new String('sss') //String对象

Number(1) // 数值类型 1
Number('sss') // 数值类型 NaN

new Number(1) // Number对象

Boolean(1) // true
new Boolean(1) //Boolean对象
new Boolean(' ') //Boolean对象

1
2
3
4
5
6
//不一致性的一个恶心的问题
var arr = new Array(3); //这个数组的length为3
//arr[0]未指定 arr[1]未指定 arr[2] 未指定
console.log(0 in arr) //false
console.log(1 in arr) //false
console.log(2 in arr) //false

JS七种数据类型

number string boolean symbol null undefined object

五个falsy值

0 NaN 空字符 null undefined

Array

用法

声明
1
2
3
var a1 = ['a','b'];   //推荐使用
var a2 = new Array('a','b')
// 两种方式没区别

不一致性 Array(3) 和 Array(3,3) (坑爹之处)

1
2
var a1 = Array(3) //单个参数  声明了length = 3 的数组 每个值是undefined
var a2 = Array(2,3) // 声明了 length = 2 的数组 [2,3]

function && Function

1
2
3
4
5
6
7
8
9
10
11
//function 是关键字  
//Function是全局对象

//声明方式
//具名函数
function fn (){ }

// 匿名函数
function(){ }

var f2 = new Function('a','b','return a+b'); //闲的蛋疼才用 很极限的情况下才会应用
1
2
var f = function (){return 'a';}
// var a = 1的变体 只不过 function存在堆里 f 存的是引用

继续研究数组

Array && 伪数组

1
2
3
4
5
6
7
8
9
10
var arr = [1,2,3];
for(var i=0;i<arr.length;i++){
console.log(arr[i])
}

//伪数组
var obj = {0:1,1:2,2:3,length:3}
for(var i=0;i<obj.length;i++){
console.log(obj[i])
}

数组与伪数组的区别就是 伪数组的原型链里没有Array.prototype这一层

forEach

1
2
3
4
5
6
var a =[1,2,3]
a.forEach(function(val,idx,arr){
//遍历方法要接受一个函数
//函数有三个参数 val是值 idx是索引 arr就是这个数组对象
console.log(val,idx,arr)
})
1
2
3
4
5
6
7
8
//接受一个函数并执行
function myExe( fn){
fn(666);
}
myExe(function(xxx){
console.log(xxx)
})
// 666

简化版forEach

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function forEach(arr,fn){
for(var i=0;i<arr.length;i++){
fn(arr[i],i);
}
}

var arr = ['a','b','c']
forEach(arr,function(v,i){console.log(v,i)})


//数组的forEach
arr.forEach(function(v,i){
console.log(v,i)
})

疑惑 为什么a.forEach() 没有像我们的forEach一样传递 arr呢?

1
2
3
arr.forEach(function(){}) 
//等价于下面的形式
arr.forEach.call(arr,function(){})

再看

1
2
3
4
5
6
7
8
var obj = {0:'a',1:'b',2:'c',length:2};
obj.forEach = function(fn){
for(var i=0;i<this.length;i++){
fn(this[i],i);
}
}
//forEach实际上接收了 obj 就是用this来接收
// js蛋疼之处

sort()

它内置的排序一般是快排

1
2
3
4
5
var a = [5,3,2,1,4];
a.sort(); // 1,2,3,4,5 默认升序
//你也可以传递一个函数 函数的返回值为正数、0或负数
a.sort(function(x,y){return x-y;}) //1,2,3,4,5
a.sort(function(x,y){function y-x};) //5,4,3,2,1

复杂的排序

1
2
3
4
5
6
7
8
9
10
hash = {
'马云':167.22,
'马化腾':379,
'李彦宏':229.11
};
//按资产排序 倒序
var a = ['马云','马化腾','李彦宏']
a.sort(function(x,y){
return hash[y] - hash[x]
})
join
1
2
3
var a = [1,2,3];
a.join() ; //1,2,3
a.join('--') //1--2--3
concat()

concat会返回一个新的数组

1
2
3
4
5
6
7
var a = [1,2,3]
var b = [4,5,6]
var c = a.concat(b);
// 返回一个新的数组 [1,2,3,4,5,6]

// 非常厉害的用法快速复制一个数组
var d =a.concat([]) // 新数组 [1,2,3]

map()

跟forEach的区别

  • forEach没有返回值
  • map有返回值
    map的含义 是映射
    1
    2
    3
    4
    var a = [1,2,3]
    a.map(function(){return value*2}) // [2,3,6]
    //箭头函数的写法
    a.map(value=>value*2)
filter

过滤

1
2
3
4
5
var a = [1,2,3,4,5,6,7,8,9,10]
a.filter(function(value){
return value%2===0;
})
// [2,4,6,8,10]

reduce

减少 降

1
2
3
4
5
6
7
8
9
10
var a = [1,2,3,4,5,6,7,8,9,10]
//第一个参数是一个函数 函数有两个参数 第一个参数 之前的结果 当前的值
//第二个参数代表初始值
a.reduce(function(sum,n){
return sum+n
},0)
// 55

//箭头函数
a.reduce((sum,n)=>sum+n,0);

map用reduce代替
1
2
3
4
5
var a  = [1,2,3];
a.reduce(function(arr,n){
arr.push(n*2);
return arr
},[]) //[2,4,6]
filter用reduce代替
1
2
3
4
5
6
7
var a  = [1,2,3,4,5,6,7,8,9,10];
a.reduce(function(arr,n){
if(n%2===0){
arr.push(n)
}
return arr
},[]) //[2,4,6,8,10]

JS里的对象

全局对象global (浏览器上叫window)

不管你在哪个网页都可以访问到这个对象—window
它有很多属性
global.parseInt()
golbal.parseFloat()

假设浏览器分给js一部分内存 如果内存里空空如也我们就什么也调用不了

每次浏览器生成的时候就生成一个global

Number

1
2
3
4
var n = 1;
var n2 = new Number(1);
// n2是 Number对象 提供很多方法 可以 n2.toString() n2.toFixed(2)
//但是 n 也可以 n.toString() n.toFixed(2) 但是 n是一个基本类型 没有方法


JS设计之初因为BOSS要求JS要像JAVA 作者为了满足老板的需求“长得像JAVA”
所以出现了 var n2 = new Number() 但实际上没有人用
JS实际用法 是 var n = 1;

但是它是怎么解决基本类型 调用方法的呢?

妙计:临时转换

1
2
3
var n  = 1;
n.toString()
// 调用toString()的时候 产生一个临时的对象 temp

  • step1 temp = new Number(n);
  • step2 temp.toSrring();
  • step3 方法调用结束后销毁 temp对象

这样我就永远不用使用new Number()去创建一个number对象 并调用其相关方法

调用过程

基本类型的xxx是多少

String对象
1
2
3
slice() // 切片方法  'hello'.slice(1,3)  输出  el   口诀包头不包尾
concat() //字符串拼接
trim() //去除首尾空格
Boolean对象

一个“坑”点

1
2
3
4
var b1 = false;
var b2 = new Boolean(false);
if(b1){console.log(1)} //不会打印1
if(b2){console.log(2)} //打印2 因为所有对象转换为boolean值都是true

Object对象
1
2
3
4
var o1  = { };
var o2 = new Object();
// o1和o2没区别 都是对象
//注意一点 凡是新声明的对象 都是不相等的 o1 !== o2

共有属性 prototype

不同类型对象都有一个toString() 和valueOf()

如果方法挂载到 对象上就会要出现很多的key

1
__proto__ //隐藏在对象里 特定的指向 该类型的共有属性



原型链 ==> 这样一层一层向上指的过程 形成了一个链条

prototype 就是共有属性


1
2
String.prototype是 String的共有属性 (这里是防止它没有,系统生成的)
s.__proto__ 是String共有属性的引用 (这个是实际使用的)

烧脑部分

1
2
3
4
5
__proto__与 prototype
// var n = new Number()
// var b = new Boolean()
// var s = new String()
// var o = new Object()


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var 对象 = new 函数()
对象.__proto__ === 函数.prototype

var s = new String()
s.__proto__ === String.prototype // true


//烧脑的来了
// 函数.prototype也是一个对象
函数.prototype.__proto__ === Object.prototype // true

//再次烧脑 函数是不是一个对象 ==》是
// var obj2 = 函数
// obj2.__proto__ === Function.prototype
函数.__proto__ === Function.prototype
Function.__proto__ === Function.prototype
// Function.prototype也是一个对象
Function.prototype.__proto__ === Object.prototype


Function.__proto__ === Function.prototype //true

结论

1
2
3
4
5
对象.__proto__  === 函数.prototype


Object.__proto__ === Function.prototype
Object.prototype.__proto__ 实际是 null

JS里的类型

类型转换

toString()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var a = 1;
var b = true;
var c = {};
var d = null;
var e = undefined;
a.toString() // "1"
b.toString() // "true"
c.toString() // "[object Object]"
d.toString() // 报错
e.toString() // 报错
//老司机怎么把不同类型变成字符串的
a + '' // "1"
b + '' // "true"
c + '' // "[object Object]"
d + '' // "null"
e + '' // "undefined"

//智障写法 不同类型是不能 + 的
// + 如果发现左右任意一边有字符串 就会把另一边转为字符串
1 + '1' // 字符 "11" 相当于 (1).toString() + '1'
1 + 1 // 数字 2

//变成字符串常用的方法是 与空字符串相加 ‘’+
//除此之外 你可以 window.String(一个变量) 与 ‘’+ 一样

Boolean()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Boolean(1) // true
Boolean(2) // true
Boolean(0) // false
Boolean('') // false
Boolean(' ') // true
Boolean('21321') // true
Boolean({a:11,b:22}) // true
Boolean({}) // true
//老司机
!! true //true
!! 1 // true
!! 0 // false
!! '' // false
!! ' ' //true
!! {} //true
!! null // false
!! undefined //false

类型转换

5个特殊值 Falsy

number string boolean symbol null undefined object
0 NaN ‘’ null undefined

转为number类型

5种方式如图
类型转换

考点

1
2
3
4
5
parseInt('011')  // 11
parseInt('011',8) // 9
parseInt('011',10) // 11
parseInt('s') // NaN
parseInt('1s') // 1

内存图

内存分为代码区和数据区
chrome为程序划分100M内存

数据区

栈 && 堆

数据区

简单示例
内存分配过程

引用

复杂类型存的是引用

示例

测试1

测试2
测试3

self 指向自己
测试4

很贱很贱的面试题

面试题

垃圾回收

GC 如果一个变量没有被引用==>它就是垃圾 ==> 将被回收

1
2
3
4
var a = {name : 'a'}   // 分配内存地址 33
var b = {name : 'b'} // 分配内存地址 60
a = b // a 的内存地址 = b 的内存地址 ==> 60
// a原来的地址 33所占用的内存空间 就是 垃圾

深拷贝 && 浅拷贝

1
2
3
var a = 1;
var b = a;
b = 2 ;// b变 a 不变 就是深拷贝(基本类型的赋值都是深拷贝)

要考虑的是 复杂类型 的深拷贝

1
2
3
var a ={ name :'a'}
var b = a ;
b.name = 'b'; //这导致 a.name也变了 (浅拷贝)

复杂类型深拷贝的概念(这里没有实现)
面试题

JS里的数据

JS如何学习

JS作者的评价

  • 原创之处并不优秀
  • 优秀之处并非原创

从简单到复杂
示意图
示意图

ES6一图概括(90%)

示意图

期待纳入规范的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
obj.a.b.c.name
//如果 obj.a 是 undefined
// obj.a.b <==> undefined.b ==>报错
//更何况 obj.a.b.c呢?
//现在的js是怎样写的
if(obj.a!==undefined&&obj.a.b!==undefined&&obj.a.b.c!==undefined&&obj.a.b.c.name!==undefined){
//终于无错的打印了变量
console.log(obj.a.b.c.name);
}

//optional chain语法
obj?.a?.b?.c?.name
//如果 obj.a是undefined就不往下走了 之间返回 undefined 写法也简洁很多

7种数据类型

Number boolean string symbol undefined null object

示意图

Number

示意图

String

示意图

处理字符串折行问题

  1. “\”转义 但是后面必须是”回车” 但是如果是 空格 你也看不出来 容易出错
  2. ‘1233’ +
    ‘456’
  3. ES6 反引号” ` “ 换行要顶头写
    示意图

boolean 两个值 true | false

布尔 true false

&& 与 || 或 运算

a&&b

a\b
×
× ×

a||b

a\b
×

symbol 略过 …

null (只有一个值 null)

undefined (只有一个值 undefined )

都表示什么也没有 (JS之父的bug)

null和 undefined的区别

1.一个变量没有赋值 ==> undefined
2.我想有一个对象 obj 但是现在还不想给值 所以推荐给他一个 null值 <==>空对象
有一个非对象 不想给值 undefined <==> 空非对象示意图

什么是object

就是由基本类型组成的复杂类型
示意图

person = {name:’hjx’,age:18,self:person}可以吗?

对象还没有初始化完成就自己指向自己
这样是对的 但是 person.self = undefined
示意图

key的值可以是 ‘’空字符吗?

示意图

delete && for in

示意图

typeof && BUG

示意图

代码封装的技巧

代码封装的技巧

拷贝一坨代码起个名字法

如下代码,明显是线性的(面向过程的)

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
$(function(){
$.ajax({
url:'xxxx',
data:{farmId:'xxxx'},
success:function(res){
// 假设 res = [1,2,3,4,5]
//数据处理 如生成列表
var arr = res;
var oUl = document.createElement('ul');
for(var i=0;i<arr.length;i++){
var oLi =document.createElement('li');
oLi.textContent = arr[i];
//监听点击事件
oLi.onclick = function(){
alert(this.textContent)
}
oUl.appendChild(oLi)
}
document.body.appendChild(oUl)

},
error:function(){
alert('error')
}
})
})

优化后如下

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
39
40
41
42
43
44
45
46
47
48
49
//1.请求数据
getData(fnSucc,fnFail);
//2.事件监听(委托)
libindEvent();

//获取请求数据
function getData(fnSucc,fnFail){
$.ajax({
url:'xxx',
success:fnSucc,
error:fnFail
})
}

function fnSucc(res){
//假设 res = [1,2,3,4,5]
var oUl = createTag('ul');
for(var i=0;i<res.length;i++){
var oLi = createLiAndSetText(res[i]);
oUl.appendChild(oLi);
}
document.body.appendChild(oUl)
}
function fnFail(err){
console.log(err)
}

//创建标签
function createTag(tagName){
var tag = document.createElement(tagName);
return tag;
}

//创建li并设置 文本
function createLiAndSetText(text){
var oLi = createTag('li');
oLi.textContent = text;
return oLi;
}

//li点击事件监听
function libindEvent(){
document.onclick = function(e){
var target = e.target;
if(target.tagName==='LI'){
alert(target.tagName)
}
}
}

改良后的代码为树状的(分层的)

  1. 当出现错误的时候我们只需要看请求是否正确
  2. 当没有出现li的时候,我们先去看数据是否回来,再去看创建li是否成功,不需要关心li的事件监听
  3. 当li点击事件出现问题时,我们只需要查看事件监听的代码

代码从线型的转换为树形结构,即使出现了bug只需要查看对应节点的代码

仔细回想二分排序算法,它每次都会帮我们筛掉一半的数据,以上代码就是很好的实现

CSS常见布局及各种居中

左右布局

左侧固定,右侧自适应

预览链接

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
/* css*/
.left {
float: left;
width: 300px;
height: 300px;
background-color: gray;
margin-right: -100%;
}
.right {
float: left;
width: 100%;
}
.right-content {
height: 300px;
margin-left: 310px;
background-color: black;
}

/*html*/
<body>
<div class="left"></div>
<div class="right">
<div class="right-content"></div>
</div>
</body>

左中右布局

左右固定,中间自适应

预览链接

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
/*css*/

#container{
position:relative;
margin:20px;
height:400px;
}
#left_side{
position:absolute;
top:0px;
left:0px;
border:solid 1px #0000FF;
width:170px;
height:100%;
}
#content{
margin:0px 190px 0px 190px;
border:solid 1px #0000FF;
height:100%;
}
#right_side{
position:absolute;
top:0px;
right:0px;
border:solid 1px #0000FF;
width:170px;
height:100%;
}

/*html*/
<div id="container">
<div id="left_side">left_side</div>
<div id="content">content</div>
<div id="right_side">right-side</div>
</div>

水平居中

  • 块级元素
1
margin:0 auto;
  • 行内元素
1
text-align:center

垂直居中

  • 行内元素
1
2
3
4
.box {
padding-top: 30px;
padding-bottom: 30px;
}

等其他小技巧

各种居中请搜索google 关键字 css center tricks

你会得到这个链接 各种居中的解决方案

CSS伪元素和技巧

什么是伪元素

如果有一个div

1
<div>hi</div>
1
2
div::before{content:'「'}
dis::after{content:'」'}

添加如下css,你会发现生成为 「hi」,这就是伪元素,你也可以改变伪元素的display为block那样它就变成了一个块元素

用CSS实现各种图形

如果你会用google就搜索「css tricks shape」

css tricks shape

如何实现颜色渐变

如果你会用google就搜索「css3 linear gradient generator」

css线性渐变生成器

生成动画

如果你会用google就搜索「css3 animation generator」

可能你没搜到那就搜索「anmiation mdn」肯定有代码给你抄

生成阴影

如果你会用google就搜索「css shadow generator」

css阴影生成器

画出太极

如果你第一次画肯定是这样

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
39
40
#taiji{
border-radius: 50%;
height: 200px;
width: 200px;
background: linear-gradient(to bottom, rgba(255,255,255,1) 0%,rgba(255,255,255,1) 50%,rgba(0,0,0,1) 50%,rgba(0,0,0,1) 100%);
}

#taiji .lbox,#taiji .rbox{
float:left;
width:100px;
height: 100px;
border-radius: 50%;
background: #fff;
margin-top:50px;
}
#taiji .rbox{
float:left;
background: black;
}
#taiji .lson,#taiji .rson{
height: 30px;
width: 30px;
background: #000;
margin:35px auto;
border-radius: 50%;
}
#taiji .rson{
background:#fff;
}

...

<div id="taiji">
<div class="lbox">
<div class="lson"></div>
</div>
<div class="rbox">
<div class="rson"></div>
</div>
</div>

如果你喜欢深挖css

预览我做的 https://sltrust.github.io/learnCss/taiji3.html

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
39
40
41
42
43
44
45
46
47
#taiji{
border-radius: 50%;
height: 200px;
width: 200px;
background: linear-gradient(to bottom, rgba(255,255,255,1) 0%,rgba(255,255,255,1) 50%,rgba(0,0,0,1) 50%,rgba(0,0,0,1) 100%);
position: relative;
animation-duration:3s;
animation-name:circlearount;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
@keyframes circlearount{
from{
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}

div#taiji::before{
content:'';
position: absolute;
height: 30px;
width: 30px;
border-radius: 50%;
background: #fff;
border:35px solid #000;
top:50px;
left:0;
}
div#taiji::after{
content:'';
position: absolute;
height: 30px;
width: 30px;
border-radius: 50%;
background: #000;
border:35px solid #fff;
top:50px;
right:0;
}

...


<div id="taiji"></div>