JS学习笔记1

2020-12-09  本文已影响0人  flyrain

todo:总结下实际中js的一些注意事项、表格、全选、切换、模态框等

原则:

语法

基本数据类型:Undefined、Null、Boolean、Number、String。使用type of可以检测数据类型

NaN:not a number 本来要返回数组的操作数未返回数值的情况,例如 10/0 = NaN

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检测引用数据类型

执行环境:

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

数组模拟队列操作:

反转:arr.reverse();

排序:arr.sort(参数是一个返回值是-1、0、1的compare函数,若不传的话则按照字符串比较排序);

concat拼接

slice(start index, end index);切片长度等同substring

splice向数组中间插入:

查找 indexof,lastIndexof

迭代:

缩小数组的方法

正则表达式 RegExp

例如: 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是指向函数的指针

函数的声明无论在何种地方都会被前置,例如:

    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程序员的角度理解闭包:

  1. 定义fun1,fun1内部返回匿名的函数,函数中引用fun1的变量(相当于引用了fun1的this.n)
  2. 执行fun2 = fun1(10)
    • "执行栈"(JS似乎没有这个概念)首先会压入global的全局上下文环境window
    • 然后压入fun1的上下文环境,其实就是fun1的局部变量n,现在栈内最底是window、栈顶是fun1.n
  3. 执行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;
}   

综上,如果在一个函数内部返回一个匿名函数要特别小心,由于函数总是携带外部环境的引用

小结:

BOM

window对象:

确认对话框confirm:

if( confirm("Are you sure?") ){
    alert("good!");
}else{
    alert("bad...");
}

输入对话框 prompt

location对象:提供与文档相关信息以及导航信息,location对象既是window对象的属性,也是document对象的属性。window.location = document.location

location对象的属性:

location.href = "http:// ..."是最常用的跳转url方法

测试输出的方法:

被声明的变量是不带值得,也就是undefined

HTML 事件是浏览器或用户做的某些事情

addEventListener() 方法:为制定元素添加制定事件处理程序,例如 document.getElementById("event").addEventListener("click", displayDate), 这种写法使得JS和HTML标记是分离的,达到更佳的可读性

JS是一门函数式语言

< script >标签最好插入在 body之前

尽可能将HTML和JS进行分离,例如HTML中的JS相关元素和代码在JS中插入preparePlaceholder这类来实现

对象:创建方式如 var person = Object(); person.name = "zhangsan";...,或者可以这样创建 var person = {}, person.name = ...; 这种使用方式也适用于person为一个数组的时候

DOM 获取特定元素的方法,属于document对象的方法:

属于节点元素的方法:

事件处理函数:

nodetype:

HTML文档全部加载完毕的时候会触发一个事件,事件的处理函数是 window.onload

插入节点:节点有元素节点和文本节点之分,例如 <元素节点 p> <文本节点 text >

Ajax:

XMLHttpRequest对象:Ajax的核心

示例:

    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的方法:

动画

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验证:

Important Function

  1. 加载时添加加载事件的addLoadEvent

        function addLoadEvent(func){
            var oldonload = window.onload;
            if( typeof window.onload != 'function' ){
                window.onload = func;
            }else{
                window.onload = function(){
                    oldonload();
                    func();
                }
            }
        }
    
  1. 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元素的方法:

ConvasRenderingContext2D的属性:

  1. canvas:获取宽度和高度
  2. fillstyle:颜色 or 图案
  3. globalAlpha:全局透明设定 完全透明0-1.0完全不透明
  4. globalCompsiteOperation:某个物体绘制在其他物体之上
  5. lineCap:线段的端点 -- butt、round、square
  6. lineWidth:~
  7. lineJoin:相交的线段焦点 bevel、round、miter(default)
  8. miterLimit:~
  9. shadowBlur : ~
  10. ....

保存以及恢复,保存在stack顶,意味着可以多次嵌套使用:

controlContext.save();

更改绘图....

controlContext.restore();

JQuery

元素选择器:

属性选择器

CSS选择器

不同的选择器之间也可以进行组合

事件:

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结构中元素发生变化

JQuery的基本筛选器

还有 filter()、not()、has()、contains()、is()....

JQuery的方法

JQuery自己独有的方法名是以一个圆点"."开头的

JQuery是引用传递的,例如var ans = $('li')代表的是JQuery记录下保存所有li标签的引用而不是直接拷贝它们的值存储

ready function:

$(document).ready(function(){});    

ready function:文档对象上的ready事件简写

$(function(){
    // xxx内部的任何语句都会在页面加载完之后自动运行
});

JQuery遍历

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);
上一篇下一篇

猜你喜欢

热点阅读