JS学习笔记1
todo:总结下实际中js的一些注意事项、表格、全选、切换、模态框等
原则:
- 渐进增强
- 平稳退化
- 低耦合
- JS脚本只应该用来充实文档的内容,要避免使用DOM技术来创建核心内容
语法
基本数据类型:Undefined、Null、Boolean、Number、String。使用type of可以检测数据类型
- Undefined类型只有一个值undefined,代表的是未初始化,例如 var msg; 仅仅对msg进行了定义
- Null类型:var msg = null; typeof msg是Object
- Boolean类型: if (msg)则msg会被自动转成Boolean值 true
NaN:not a number 本来要返回数组的操作数未返回数值的情况,例如 10/0 = NaN
- 任何涉及到NaN的操作都会返回NaN,如 NaN/10
- NaN不等于任何值包括它本身, NaN == NaN是false
isNaN() | 结果 |
---|---|
NaN | true |
10 | false |
"10" | false |
"blue" | true |
true | false |
控制流:
支持for-in : for (each in items){ each.toString();}
函数的参数:函数的参数可以随意写,最终都是对arguments数组进行处理,传入的参数和arguments对应的元素的值是同步更新的
function fun(){
console.log(arguments[0]+" test ");
}
fun(1);
output: 1 test
---
fun(1,3,4,5);
output: 1 test
typeof检测基本数据类型,instanceof检测引用数据类型
执行环境:
- 全局执行环境:window
- 每个函数自己的执行环境
JS没有块级作用域,例如
if(true){
var msg = "var in if";
}
alert(msg); // success,if将msg变量声明添加到当前的全局环境
//同理
for(var i=0 ; i<10 ; i++){
xxx..
}
alert(i); //10
使用var声明的变量会被自动添加到最接近的环境中去,函数内部 =>局部环境
创建对象:
var person = {
name : "zhangsan",//不是 =
age : 11,
"sex" : "male" //属性名可以使用字符串或者非字符串
};
//或者如下
person.name = "zhangsan";
person.age = 11;
...
//输出如下
console.log(person["name"]);
console.log(person.name);
//chrome test
var person = { name = "zhangsan", first = "zhang" };
VM625:1 Uncaught SyntaxError: Invalid shorthand property initializer
var person = { name : "zhangsan", first : "zhang" };
undefined
person.name
"zhangsan"
var person = { name : "zhangsan", first name: "zhang" };
VM678:1 Uncaught SyntaxError: Unexpected identifier
var person = { name : "zhangsan", "first name": "zhang" };
undefined
person.first name
VM766:1 Uncaught SyntaxError: Unexpected identifier
person["first name"]
"zhang"
创建数组,数组中每个元素的类型可以不一致:
var colors = ["red", "blue", "green"];
var names = [];
alert(colors.length);
//可以更改length
colors.length = 2;// "green"不可见
//添加操作可以这么写
colors[colors.length] = 'black';
check是不是数组:Array.isArray( arr );
var arr = ["zhangsan", "lisi", "wangwu"];
console.log(arr.join(' '));
VM1085:1 zhangsan lisi wangwu
数组模拟栈操作: push等价于arr[length] = value、pop
数组模拟队列操作:
- 队尾入,队头出:push、shift,从队尾推入,从队头删除shift,push操作和栈操作的push是相同的
- 队头入,队尾出:unshift、pop
反转:arr.reverse();
排序:arr.sort(参数是一个返回值是-1、0、1的compare函数,若不传的话则按照字符串比较排序);
concat拼接
slice(start index, end index);切片长度等同substring
splice向数组中间插入:
- 删除:splice(start index, end index)
- 插入:splice(start index, 0 , 插入项1 Object1, 插入项2...)
- 替换: splice(start index, 删除的项数目 cnt,插入项1 Object1, 插入项2...)
查找 indexof,lastIndexof
迭代:
- every,有返回值的forEach,每一项都返回true才返回true
- filter
- forEach
- map:返回执行结果数组
- some:任一返回true,则返回true
缩小数组的方法
- reduce 第一项遍历到最后
- reduceRight 最后一项开始到第一项
正则表达式 RegExp
- g:全局global
- i:不区分大小写ignoreCase
- m:多行,继续查找下一行multiline
例如: var text = "cat nat ddd"; var pattern1 = /.at/g; pattern没有引号,/开头,代表匹配所有at结尾的3个字符 var matches = pattern.exec(text); 返回匹配的数组
Function类型
函数实际上也是对象,每个函数都是Function类型的实例,例如:
var sum = new Function("n1", "n2", return "n1+n2");sum是指向函数的指针
- 这也解释了 var anotherSum = sum;这种赋值,由于JS里同样都是值传递,所以anotherSum其实是拷贝了sum的指针
- 同样解释了为什么没有重载,sum = new Function( xx..); sum = new Function( xxx..xx.. );及时两次赋值的Function参数签名不同,引用永远是指向第二个Function
函数的声明无论在何种地方都会被前置,例如:
alert(sum(1,2)); //可以正常执行
function sum(n1, n2){return n1+n2;}
alert(sum(1,2)); //不可以正常执行,sum是一个指针而非声明
var sum = function(n1, n2){return n1+n2;}
callee属性:指向拥有这个arguments对象的函数。为什么要有这个属性,是将函数名抽象出来方便递归,
例如 :
function fun1(n){
xx...;
return fun1(n-1);//如果fun2 = fun1,执行fun2(3) => return fun1(2);造成函数间的耦合
}
function fun1(n){
xx...;
return arguments.callee(n-1);// fun2 = fun1,执行fun2(3) => return fun2(2);fun1的改动不影响fun2
}
this对象:引用的是函数执行的环境对象,全局就是window
prototype属性:保存它们所有实例方法例如 toString(), valueOf(),类似Java中的Object
functionName.call(作用域)可以扩充函数运行的作用域
window.color = "red";
var o = { color : "black"};
function sayColor (){ alert(this.color); }
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //black
//或者使用bind
var bindSayColor = sayColor.bind(o);
bindSayColor.sayColor; // blue
单体内置的对象,自带的可以拿来直接使用的:
Global对象:全局作用域中定义的属性和函数都是Global对象的属性
window对象:web浏览器实用的全局对象
Math对象:min、max、ceil、floor
OOP
成员属性行为特性的描述:Configurable、Enumerable(for-in迭代开关)、Writable(属性修改开关)、Value
构造函数如果不适用new关键字的话: Person("zhangsan", 36); 这就是一个普通的函数,this指向全局变量window。构造函数中引用的函数可以放在构造函数外部通过this关键字来进行关联,例如:
function Person(name, age){
//成员属性
this.name = name;
...
//function say(){}可以放在全局变量中定义
this.say = say;//Person的say方法引用全局的say方法,全局的say方法可以重用,但是会丢失封装的特性
}
function say(){
do something;
}
原型模式:可以解决继承的代码封装问题,约等于父类
每个函数都有一个prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。类似于这个类的父类,可以使用prototype来制定原型的属性:
function Person(){
}
Person.prototype.name = "zhangsan";
var p1 = new Person();// name = "zhangsan"
var p2 = new Person();// name = "zhangsan"
//更好的封装是这样
Person.prototype = {
name : "zhangsan",
age : 18,
say : function(){
do something;
}
}
prototype可以以简单理解为父类,子类p1中的同名属性会覆盖父类,如果子类中不存在的属性会去父类中继续搜索,特别的 可以通过delete来屏蔽子类的同名属性
p1.name;
"lisi"
p1.name = null;
p1.name;
null
delete p1.name;
true
p1.name;
"zhangsan"
hasOwnProperty方法可以检测属性是在实例中还是原型中,存在实例中返回true,参数要用"str"传入
p1.hasOwnProperty(name);
false
p1.hasOwnProperty("name");
true
"name" in p1;
true
"name" in Person;
true
原型的引用属性也是共享的,也就是更改的操作是同步的,所以不共享的对象或者数组需要在实例自己的构造函数中实现
稳妥构造函数模式:
function Person(name, age){
var o = new Object();
o.say = function(){
//外部无法访问 name 属性
alert(name);
};
return o;
}
函数表达式
function fun(){
do something;
}
var fun = function(){ do something; };
//这两种声明的方式区别在于第二种方式声明了一个匿名的函数并将它赋值给fun,所以无法享受函数声明提升,也就说说不能如下这样使用
fun();
var fun = function(){ do something; };
//函数声明提升意味着不能写同名的可提升函数
if( exp ){
function fun1(){ do something };
}else{
function fun1(){ do other thing };
}
//由于这两个函数声明都会被提升,所以可能造成无法预知的错误,这里只能使用匿名函数赋值的操作
闭包:有权访问另一个函数作用域中的变量的函数,创建闭包的方式就是在一个函数内部创建另一个函数
function fun1(n){
return function(){
return n+n; // 在这个匿名函数内部访问了外部的变量n
}
}
//chrome执行情况:
function fun1(n){
return function(){
return n+n;
}
}
结果 undefined
fun2 = fun1(10);
结果 ƒ (){
return n+n;
}
fun2(3);
结果 20
如上一个示例从Java程序员的角度理解闭包:
- 定义fun1,fun1内部返回匿名的函数,函数中引用fun1的变量(相当于引用了fun1的this.n)
- 执行fun2 = fun1(10)
- "执行栈"(JS似乎没有这个概念)首先会压入global的全局上下文环境window
- 然后压入fun1的上下文环境,其实就是fun1的局部变量n,现在栈内最底是window、栈顶是fun1.n
- 执行fun2(3)
- 执行栈压入fun2的上下文环境,fun2是fun1 return的匿名函数没有参数,所以传入的3实际没用
- 执行栈开始执行
ƒ (){
return n+n;
} - 查找n的值,找到fun1.n = 10,传入n = fun1.n = 10;
- return 20
[图片上传失败...(image-bcb4f3-1607516175456)]
另一个例子:
funtion createFunctions(){
var res = new Array();
for( var i=0 ; i<10 ; i++){
res[i] = function(){
return i;
};
}
return res;
}
//chrome执行如下
var n1 = arr[0];
var n2 = arr[1];
n1();
结果 10
n2();
结果 10
同理n1(),也就是返回的匿名函数在执行的时候会去执行栈(JS中其实没有执行栈,是引用链)中查找 i 的值并返回,此时 递归查找到 createFunctions的内部变量i = 10 返回 10
//正确做法
funtion createFunctions(){
var res = new Array();
for( var i=0 ; i<10 ; i++){
res[i] = function(num){
return function(){
return num;
};
}(i);//每次循环时传入i, num = i
}
return res;
}
综上,如果在一个函数内部返回一个匿名函数要特别小心,由于函数总是携带外部环境的引用
小结:
- 函数表达式不同于函数声明,函数表达式可以不需要名字
- 递归函数应当使用arguments.callee来递归调用
- 函数返回一个闭包,这个函数的作用域(类似前面说的执行栈)将会一直在内存中保存到闭包不存在为止
- 创建并立即执行一个函数,这样既可以执行其中的代码,又不会在内存中留下该函数的引用
BOM
window对象:
- JS访问浏览器窗口的一个接口
- JS中的Global对象
确认对话框confirm:
if( confirm("Are you sure?") ){
alert("good!");
}else{
alert("bad...");
}
输入对话框 prompt
location对象:提供与文档相关信息以及导航信息,location对象既是window对象的属性,也是document对象的属性。window.location = document.location
location对象的属性:
- hash:url的hash
- host:服务器名称和(端口号),没有端口号就是域名
- href:完整url
- pathname
- port
- protocol
- search
location.href = "http:// ..."是最常用的跳转url方法
测试输出的方法:
- document.write()
- innerHTML
- console.log()
- window.alert()
被声明的变量是不带值得,也就是undefined
HTML 事件是浏览器或用户做的某些事情
- onchange : HTML 元素已被改变
- onclick
- onmouseover
- onmouseout
- onkeydown
- onload : 浏览器已经完成页面加载
- ....
addEventListener() 方法:为制定元素添加制定事件处理程序,例如 document.getElementById("event").addEventListener("click", displayDate), 这种写法使得JS和HTML标记是分离的,达到更佳的可读性
- 这里的displayDate是一个处理函数,也可以直接写成函数的内容如function(){ alert("Hello World!");
JS是一门函数式语言
< script >标签最好插入在 body之前
尽可能将HTML和JS进行分离,例如HTML中的JS相关元素和代码在JS中插入preparePlaceholder这类来实现
对象:创建方式如 var person = Object(); person.name = "zhangsan";...,或者可以这样创建 var person = {}, person.name = ...; 这种使用方式也适用于person为一个数组的时候
DOM 获取特定元素的方法,属于document对象的方法:
- getElementById("xx")
- HTML: #
- < div id="xx" ...
- 返回单个对象
- JQuery: $('#xx_id')
- getElementsByTagName("li")
- HTML: .
- 返回 对象数组
- 可以传参getElementsByTagName("*")
- JQuery: $('.xx_class')
- getElementsByClassName("xxx")
- HTML:
- 低版本浏览器可能不支持
- JQuery: $('tag')
属于节点元素的方法:
- getAttribute方法: var v = document.getElementById("xx"); v.getAttribute("title");
- setAttribute方法
事件处理函数:
- 鼠标悬停触发: onmouseover
- 鼠标离开触发:onmouseout
- 点击触发:onclick
nodetype:
- 元素节点的值为 1
- 属性节点的值为 2
- 文本节点的值为 3
HTML文档全部加载完毕的时候会触发一个事件,事件的处理函数是 window.onload
插入节点:节点有元素节点和文本节点之分,例如 <元素节点 p> <文本节点 text >
-
创建节点createElement空的P元素相当于"< p >": var pElement = document.createElement("p");//创建一个P元素
-
插入文档的节点树appendChild: parent.appendChild(child)
-
createTextNode创建文本节点 < p > < text >: var text = document.createTextNode("hello world"); para.appendChild(text)
-
插入位置xx之前: insertBefore:参数为 插入的newElement,目标元素targetElement,父元素parentElement, parentElement.insertBefore(newElement, targetElement)
-
插入示例:
var gallary = document.getElementById("imagegallery"); gallary.parentNode.insertBefore(placeholder, gallary);
-
-
插入位置xx之后:找到target的后一个,然后insertBefore
function insertAfter(newElement, targetElement){ var parent = targetElement.parentNode; if(parent.lastChild == targetElement){ parent.appendChild(newElement); }else{ parent.insertBefore(newElement, targetElement.nextSibling); } }
Ajax:
XMLHttpRequest对象:Ajax的核心
- 浏览器脚本 < = > XMLHTTPRequest < = > 服务器
示例:
function getHTTPObject(){
if( typeof XMLHttpRequest == "undefined")
//尝试对IE浏览器的不同版本获取对象
XMLHttpRequest = function(){
try{
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
}catch(e){}
try{
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
}catch(e){}
try{
return new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){}
return false;
}
return new XMLHttpRequest();
}
XMLHttpRequest对象的 open方法:制定服务器上将要访问的文件,Method = 'GET', 'POST', 'SEND',例如使得页面加载时候请求:
function getNewContent(){
var request = getHTTPObject();
if(request){
//发起get请求,请求同目录的example.txt
request.open("GET", "example.txt", true);
//onreadystatechange是事件处理函数,服务器给XMLHTTPRequest对象响应的时候触发执行,根据服务器的具体响应做响应的处理,means下面声明onreadystatechange引用函数
request.onreadystatechange = function(){
if(request.readyState == 4){
//拿到request的responseText
var txt = document.createTextNode(request.responseText);
//插入DOM
xxx..
}
};// function 赋值给 onreadystatechange
request.send(null);
}else{
alert('Sorry, your browser doesn\'t support XMLHttpRequest');
}
}
//添加到Load Event
addLoadEvent(getNewContent);
Ajax的使用也是渐进式而非颠覆式的,最好可以遵循前面的平滑退化原则
如需像 HTML 表单那样 POST 数据,请通过 setRequestHeader() 添加一个 HTTP 头部。请在 send() 方法中规定您需要发送的数据:
xhttp.open("POST", "ajax_test.asp", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send("fname=Bill&lname=Gates");
open的最后一个参数true代表异步,false代表的是同步
CSS相关
引用带减号的CSS属性时,DOM要求使用驼峰命名法:如 font-family => fontFamily,即 element.style.fontFamily
通过DOM改变CSS的方法:
- 取出元素 elem.style.fontSize = "1.2em"; 应该仅用于负责的用单纯的CSS难以定位的某些元素
- 给与另一个ClassName,或者是追加另一个ClassName: elem.className += " intro"; intro前面有空格!
动画
setTimeout:让某个函数在经过一段预定的时间之后才开始执行,var variable = setTimeout("functionName", interval-毫秒);
取消:clearTimeout(variable)
实现动画 => 改变CSS的属性,如距离, elem.style.left = "10px" => elem.style.left = "100px";
function moveElement(elementId , target_x, target_y, interval) {
if(!document.getElementById) return false;
if(!document.getElementById(elementId)) return false;
var element = document.getElementById(elementId);
//避免多次移动同一个对象造成的多个movement的重叠冲突
if (element.movement){
clearTimeout(element.movement);
}
if (!element.style.left){
element.style.left = "0px";
}
if (!element.style.top){
element.style.top = "0px";
}
var x = parseInt(element.style.left);
var y = parseInt(element.style.top);
if( x == target_x && y == target_y){
return true;
}
if( x < target_x){
dist = Math.ceil((target_x - x)/10);
x += dist;
}
if( x > target_x){
dist = Math.ceil((x - target_x)/10);
x -= dist;
}
if( y < target_y){
dist = Math.ceil((target_y - y)/10);
y += dist;
}
if( y > target_y){
dist = Math.ceil((y - target_y)/10);
y -= dist;
}
element.style.left = x + "px";
element.style.top = y + "px";
// moveElement('elementId',target_x,target_y,interval); elementId 是一个str,需要加上''
var repeat_str = "moveElement('" +elementId +"'," +target_x + "," + target_y +","+interval+")";
element.movement = setTimeout(repeat_str, interval);
}
表单提交JS验证:
- 拦截提交: thisform = document.forms[i]; resetFields(thisform); thisform.onsubmit = funtion(){ return 验证函数function(this)}
- onsubmit事件处理函数来处理提交,如果验证函数function返回true意味着可以提交表单数据,false则取消提交操作
- 可以使用Ajax来显示成功与否
Important Function
-
加载时添加加载事件的addLoadEvent
function addLoadEvent(func){ var oldonload = window.onload; if( typeof window.onload != 'function' ){ window.onload = func; }else{ window.onload = function(){ oldonload(); func(); } } }
-
insertAfter:
function insertAfter(newElement, targetElement){ var parent = targetElement.parentNode; if(parent.lastChild == targetElement){ parent.appendChild(newElement); }else{ parent.insertBefore(newElement, targetElement.nextSibling); } }
Canvas
应该使用:400后面没有px
<canvas id="canvas" width='400' height='400'></canvas>
不要使用
<canvas id="canvas" style = " width ....."></canvas>
通过CSS设定的大小只对元素本身有效,在HTML canvas元素中设定时对元素本身和元素绘图表都有效,避免了元素与绘图表面大小不同造成的缩放
canvas元素的方法:
- getContext() :上下文环境对象
- toDataURL(type, quality):返回一个数据地址
- toBlob(callback, type, args...):创建一个用于此canvas元素图像文件的Blob,第一个参数是回调函数
ConvasRenderingContext2D的属性:
- canvas:获取宽度和高度
- fillstyle:颜色 or 图案
- globalAlpha:全局透明设定 完全透明0-1.0完全不透明
- globalCompsiteOperation:某个物体绘制在其他物体之上
- lineCap:线段的端点 -- butt、round、square
- lineWidth:~
- lineJoin:相交的线段焦点 bevel、round、miter(default)
- miterLimit:~
- shadowBlur : ~
- ....
保存以及恢复,保存在stack顶,意味着可以多次嵌套使用:
controlContext.save();
更改绘图....
controlContext.restore();
JQuery
元素选择器:
- "#id"
- ".class"
- "element"
属性选择器
- "[href]"
- "[href = '/url/...']"
- "[href != '/url/...']"
- "[href $= '.jpg' ]" href以".jpg"结尾的元素
CSS选择器
- $("p").css("background-color", "red");
不同的选择器之间也可以进行组合
事件:
- $(document).ready(function)
- $(selector).click(function)
- $(selector).focus(function) //selector获得焦点事件
- $(selector).mouseover(function) //selector鼠标悬停事件
toggle可以控制显示、隐藏,参数可取"slow","fast"或者毫秒
获得输入框的值,JQuery可以通过val很方便的获取元素的value
<!DOCTYPE html>
<html>
<head>
<script src="/jquery/jquery-1.11.1.min.js"></script>
<script>
$(document).ready(function(){
$("button").click(function(){
alert("Value: " + $("#test").val());
});
});
</script>
</head>
<body>
<p>姓名:<input type="text" id="test" value="米老鼠"></p>
<button>显示值</button>
</body>
</html>
text()、html()、val()同样有回调函数,see https://www.w3school.com.cn/jquery/jquery_dom_set.asp
事件监听:
在HTML上写事件处理不符合分离原则,使用一级DOM事件只能绑定一个处理函数,element.onclick = functionName,在二级DOM中使用事件监听器可以多个函数处理,element.addEventListener('click', functionName, false);相应的可以removeEventListener,(因为只能传函数名不能传函数的参数,所以传参需要使用匿名函数 )
变动事件和变动观察者:针对DOM结构中元素发生变化
- DOMNodeInserted
- DOMNodeRemoved
- DOMSubtreeModified:insert或者remove触发
- DOMNodeInsertedIntoDocument
- DOMNodeRemovedFromDocument
JQuery的基本筛选器
- not
- first
- last
- even
- add
- eq、gt、lt:等于、大于、小于
- header:所有h1到h6的元素
- focus
还有 filter()、not()、has()、contains()、is()....
JQuery的方法
JQuery自己独有的方法名是以一个圆点"."开头的
JQuery是引用传递的,例如var ans = $('li')代表的是JQuery记录下保存所有li标签的引用而不是直接拷贝它们的值存储
ready function:
$(document).ready(function(){});
ready function:文档对象上的ready事件简写
$(function(){
// xxx内部的任何语句都会在页面加载完之后自动运行
});
JQuery遍历
- parent()
- parents():层层向上,递归寻找父节点
- parentsUntil():给定元素之间的所有父元素
- children():可以过滤,例如children("p .1"),代表元素为p,类名为1的子元素
- find()
- siblings():兄弟元素all
- next()
- nextAll()
- first()
- last()
- filter()
JQuery核心函数 $()
传入一个字符串的时候会生成一个元素,这里面传入一个DOM元素也是同样的效果,例如
var $p = $("<p> text......</p>");
//或者
var p = document.createElement("<p>....</p>");
var $p = $(p);
JQuery是一个匿名的函数,格式为:
(function(window,undefined){})(window);
//前面的(function(window,undefined){})相当于一个匿名函数
fun(window);