JavaScript高级知识点整理(代码含解析)

JavaScript 高级笔记

面向对象与面向过程

面向过程是指分析结局问题的步骤,然后按照分析的步骤一步步实现.

面向对象是指把事物分解成一个个对象,然后由对象之间分工合作.

  面向过程 面向对象
优点 性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护
缺点 不易维护、不易复用、不易扩展 性能比面向过程低

面向对象的特点

封装 继承 多态

对象和类

对象是由属性和方法组成:是一个无序键值对的集合,是指一个具体的事物

(对象如果想批量生产出来,可以用类似现实生活中的图纸和车的关系一样

图纸:类 ; 车:对象)

属性:事物的特征,在对象中用属性来表示(常用名词)

方法:事物的行为,在对象中用方法来表示(常用动词)

//创建对象的三种简单方式:
1.字面量:
var obj={
    age :18,//属性名:属性值
    uname :'Amy'
    fn:function(){
        console.log('你好')
    }//方法名:方法
}
2.构造函数:
function Person(name,age){
    this.name = name;
    this.age = age;
    this.sayHi=function(){
        console.log('你好')
    }
}
var xm = new Person('小明',20)
​
3.自定义构造函数:
var obj = new Object();
obj.name='小明';//给对象追加属性
obj.sayHi=function(){
        console.og('你好')
    }//给对象追加方法
  工厂模式 自定义构造函数
不同 函数名是小写 有new, 有返回值 new之后的对象是当前的对象 直接调用函数就可以创建对象 函数名是大写(首字母) 没有new 没有返回值 this是当前的对象 通过new的方式来创建对象
相同 都是函数,都可以创建对象,都可以传入参数  
<script>
        // 工厂模式
        function fn(name, age) {
            var obj = {}
            obj.name = name
            obj.age = age
            return obj;
​
        }
        var zxy = fn('张学友', 20)
        console.log(zxy);
        // 构造函数 ----->实例化对象
        function Star(name, age) {
            this.name = name
            this.age = age
            this.sing=function(){
                console.log('我会唱歌')
            }
        }
        var ldh = new Star('刘德华', 20)
        console.log(ldh);
    /*
    实例对象和构造函数之间的关系:
    * 1. 实例对象是通过构造函数来创建的---创建的过程叫实例化
    如何判断对象是不是这个数据类型?
       1) 通过构造器的方式 实例对象.构造器==构造函数名字  
       hero.constructor === Hero
       2) 对象 instanceof 构造函数名字尽可能的使用第二种方式来识别
   */
    </script>

实例成员与静态成员

//实例成员是对象的成员
hero.name = 'zs';
//静态成员是直接给构造函数添加的成员
Hero.version = '1.0';
​
// 静态成员不能使用对象的方式来调用
console.log(hero.version);
​
// 静态成员使用构造函数来调用
console.log(Hero.version);
​
//实例成员 是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问
 function Star(uname, age) {
     this.uname = uname;
     this.age = age;
     this.sing = function() {
     console.log('我会唱歌');
    }
}
var ldh = new Star('刘德华', 18);
console.log(ldh.uname);//实例成员只能通过实例化的对象来访问
​
//静态成员  在构造函数本身上添加的成员  如下列代码中 sex 就是静态成员,静态成员只能通过构造函数来访问
 function Star(uname, age) {
     this.uname = uname;
     this.age = age;
     this.sing = function() {
     console.log('我会唱歌');
    }
}
Star.sex = '男';
var ldh = new Star('刘德华', 18);
console.log(Star.sex);//静态成员只能通过构造函数来访问

ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象.

1.创建类:语法
//使用class关键字
class name {
    //class student
}
//使用定义的类创建实例 使用new关键字
var xm = new name();
​
2.示例
 class Star {
            // constructor在NEW的时候会自动被调用,并且只能调用一次
            // 公共的方法在new的时候不会被调用,只能我们自己主动调用
     // 类的共有属性放到 constructor 里面 constructor是 构造器或者构造函数
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
            // 公共方法
            // 类中所有的函数都不需要用function,并且不需要逗号隔开
            sing(song) {
                console.log('我会唱' + song);
            }
        }
        //利用类创建对象 new
        var ldh = new Star('刘德华', 20);
​
        ldh.sing('笨小孩');
/*
注意点
1. 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写
2. 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象
3. constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数
4. 多个函数方法之间不需要添加逗号分隔
5. 生成实例 new 不能省略
6. 语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需要加function
*/

类的继承

语法:
// 父类
class Father{   
} 
​
// 子类继承父类
class  Son  extends Father {  
}   
​
示例:
 //继承
      class Father {
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
                this.money1 = 300;
            }
            sing1() {
                return '我会唱歌';
            }
            eat() {
                console.log('我还会吃饭');
            }
            sleep() {
                console.log('我会睡觉');
            }
        }
        // sing1()
        // extends继承父类
        class Son extends Father {
            constructor(uname, age, money) {
                // super()调用父类的 constructor构造函数
                super(uname, age, money);
                this.money = money;
            }
            hobby() {
                console.log('我喜欢看书' + super.sing1());
            }
            salary() {
                console.log(this.money + this.money1);
            }
        }
        var xm = new Son('小明', 22, 100);
        // console.log(xm);
        xm.salary();//400
        xm.hobby();//我喜欢看书我会唱歌
/*
注意:
1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
3. 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用父类的构造函数,super 必须在子类this之前调用
4. 时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用.
   1. constructor中的this指向的是new出来的实例对象 
   2. 自定义的方法,一般也指向的new出来的实例对象
   3. 绑定事件之后this指向的就是触发事件的事件源
5. 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
​
*/

tab栏切换案例

<style>
        * {
            margin: 0;
            padding: 0;
        }
​
        .content {
            width: 50%;
            height: 400px;
            border: 1px solid #000;
            margin: 100px auto;
            position: relative;
        }
​
        .content .top {
            height: 70px;
            border-bottom: 1px solid green;
​
​
        }
​
        .content .top ul {
            overflow: hidden;
        }
​
        .content .top ul li {
            position: relative;
            float: left;
            list-style: none;
            width: 60px;
            text-align: center;
            line-height: 70px;
            border-right: 1px solid red;
​
        }
​
        .content .top ul li i {
            background-color: blue;
            position: absolute;
            right: 0;
            top: 0;
            line-height: normal;
            font-style: normal;
            cursor: pointer;
        }
​
        .content .top .liactive {
            background-color: pink;
​
        }
​
        .add {
            position: absolute;
            top: 25px;
            right: 0;
​
        }
​
        .content .bottom>div {
            display: none;
        }
​
        .content .bottom>div.dactive {
            display: block;
        }
    </style>
</head>
​
<body>
    <!-- 大盒子 -->
    <div class="content" id="content">
        <!-- 上面的标题部分 -->
        <div class="top">
            <ul>
                <li class="liactive"><span>1标签</span><i>x</i></li>
                <li><span>1标签</span><i>x</i></li>
                <li><span>1标签</span><i>x</i></li>
            </ul>
            <button class="add">+</button>
        </div>
        <!-- 下面的内容 -->
        <div class="bottom">
            <div class="dactive">1内容</div>
            <div>2内容</div>
            <div>3内容</div>
        </div>
    </div>
    <script>
        // 创建一个类,存放公共的方法
        class Tab {
            constructor(id) {
                this.main = document.querySelector(id);
                this.init();
            }
            init() {
                // 获取标题的大盒子
                this.titleBox = this.main.querySelector('.top ul');
                // 获取标题的列表
                this.titleList = this.main.querySelectorAll('.top li');
                // 获取下面存放内容的大盒子
                this.contentBox = this.main.querySelector('.bottom ');
                // 获取下面存放内容的列表
                this.contentList = this.main.querySelectorAll('.bottom>div');
                // 添加按钮
                this.addBtn = this.main.querySelector('.add');
                // 删除按钮
                this.removeBtn = this.main.querySelectorAll('li i')
                this.toggle();
                this.add();
                this.remove();
                this.revise();
            }
            // 切换
            toggle() {
                var that = this;
                // 排他思想,点击事件
                for (var i = 0; i < this.titleList.length; i++) {
                    // 设置自定义属性
                    this.titleList[i].setAttribute('index', i);
                    // 点击事件开始
                    this.titleList[i].onclick = function () {
                        for (var j = 0; j < that.titleList.length; j++) {
                            // 点击的时候先清空类名,确定点击哪一个的时候把类名给附给哪一个
                            that.titleList[j].className = '';
                            that.contentList[j].className = '';
                        }
                        // 点击的标签添加类名
                        this.className = 'liactive';
                        // 定义一个index 然后获取到点击的索引值
                        var index = this.getAttribute('index');
                        // 内容列表相同的下标的页面添加相同的类名显示出来
                        that.contentList[index].className = 'dactive';
​
                    }
                }
            }
            // 添加
            add() {
                var that = this;
                this.addBtn.onclick = function () {
                    // console.log('123');
                    var newli = '<li><span>新标签</span><i>x</i></li>';
                    var newdiv = '<div>新内容</div>';
                    // console.log(this);//指向add
                    // console.log(that);//指向tab
                    that.titleBox.innerHTML += newli;
                    that.contentBox.innerHTML += newdiv;
                    that.init();
                    that.titleList[that.titleList.length - 1].click();
                }
            }
            // 删除按钮
            remove() {
                var that = this;
                for (var i = 0; i < this.removeBtn.length; i++) {
                    this.removeBtn[i].setAttribute('index', i);
                    this.removeBtn[i].onclick = function (e) {
                        e.stopPropagation();
                        // console.log('123');
                        // console.log(this);
                        // console.log(that);
                        var index = this.getAttribute('index');
                        that.titleList[index].remove();
                        that.contentList[index].remove();
                        // that.init();
                        if (index == 0) {
                            // return;
                            if (that.titleList.length == 1) {
                                return;
                            } else {
                                that.init();
                                that.titleList[0].click();
                            }
                        }
                        // 如果删除以后发现选中的还在的话,就不需要切换
                        if (that.main.querySelectorAll('.liactive').length > 0) {
                            return;
                        }
                        index--;
                        that.titleList[index].click();
                    }
                }
​
​
            }
            // 修改标签
            revise() {
                var that = this;
                for (var i = 0; i < this.titleList.length; i++) {
                    this.titleList[i].setAttribute('index', i);
                    this.titleList[i].ondblclick = function () {
                        // console.log('123');
                        // console.log(this);//指向的是li标签                       
                        var newText = this.innerHTML;
                        this.innerHTML= `<input type="text" >`;
                        var input = this.children[0];
                        input.value = newText;
                        input.focus()
                        input.onblur = function () {
                            this.parentNode.innerHTML = this.value
                        }
                        console.log(input);
                        console.log(newText);
​
                    }
                }
                
            }
        }
        new Tab('#content');
    </script>
</body>

原型(原型链)

//构造函数 原型对象 实例对象三者的关系
/*
* 构造函数可以实例化对象
* 构造函数中有一个属性叫prototype,是构造函数的原型对象
* 构造函数的原型对象(prototype)中有一个constructor构造器,这个构造器指向的就   是自己所在的原型对象所在的构造函数
* 构造函数的原型对象(prototype)中的方法是可以被实例对象直接访问的
* 实例对象的原型对象(__proto__)指向的是该构造函数的原型对象
*/
/*
实例对象.__proto__ ===>构造函数的原型对象
实例对象.__proto__.__proto__ ===>Object的原型对象
实例对象.__proto__.__proto__.__proto__ ===>null
实例对象 = new 构造函数
构造函数.prototype ===>构造函数的原型对象
原型对象.constructor ===>构造函数
实例对象.constructor ===>构造函数
原型对象.__proto__ ===>Object的原型对象
原型对象.__proto__.constructor ===>Object
*/

this指向

/*构造函数中的this就是实例对象
原型对象中方法中的this就是实例对象*/
   function Mother(name, age) {
            this.name = name
            this.age = age
        }
        
        Mother.prototype.fn1 = function(){
            console.log(this);
        }
        var son = new Mother('小明', 20)
        son.fn1();

原型添加方法

不需要共享的数据写在构造函数中,需要共享的数据写在原型中(实例对象可以直接访问原型对象中的属性和方法)

原型的作用: 1. 数据共享, 节省内存空间 2. 实现继承

/*构造函数里面的方法会在每次new一个新对象的时候, 都存储一次该方法, 这样就会造成内存冗杂
解决1: 把这个方法sayHi写成全局方法, 在构造函数中this.sayHi = sayHi;
这样可能会造成函数名重复, 更麻烦
况且如果此时有10个方法需要存储, 那么需要在全局作用域里面写10个函数, 也比较占内存
解决2: 原型 
每一个构造函数都有一个属性   原型 / 也叫原型对象
可以给对象动态增加方法*/
function Student(name, age, sex) {
      this.name = name;
      this.age = age;
      this.sex = sex;
    } 
​
    Student.prototype.sayHi = function () {
      console.log('大家好,我是' + this.name);
    }
​
 //通过Student构造函数,创建的对象,可以访问Student.prototype中的成员
    var s1 = new Student('lilei', 18, '男');
    var s2 = new Student('hmm', 18, '女');
    s1.sayHi();
    s2.sayHi();
/*当调用对象的属性或者方法的时候,先去找对象本身的属性/方法 ,如果对象没有该属性或者方法。此时去调用原型中的属性/方法
如果对象本身没有该属性/方法,原型中也没有该属性或者方法,此时会报错*/
​
/*
1. 对象的__proto__ 等于  构造函数的Student.prototype
2. s1.__proto__ === Student.prototype   //true
3. __proto__属性是非标准的属性->生产环境不能使用
4. 在原型对象中有一个属性 constructor  构造函数
5. constructor  作用记录了创建该对象的构造函数  记录了创建该对象的构造函数
6. s1.constructor === Student   //true
7. 实例对象可以直接访问原型对象中的属性和方法
*/
//如果需要增加的方法多的话, 都写给prototype就太复杂了可写成
Student.prototype = {
      sayHi: function () {
        console.log('sayHi');
      },
      eat: function () {
        console.log('eat');
      }
    } 
​
//可是这样的话, Student.prototype就是一个新的对象了, 不能指回Student.prototype的构造函数, 所以必须修改为:
    Student.prototype = {
      constructor: Student,
      sayHi: function () {
        console.log('sayHi');
      },
      eat: function () {
        console.log('eat');
      }
    }
/*
   // test属性在原型对象上,而在设置属性的值的时候,不会搜索原型链
   // 而是直接给对象新增一个test属性
   s1.test = '123xxx';
   此时s1自身会增加一个test属性, 并且赋值为123xxx
   而原型链里面也有一个test属性, 只不过被s1自身的test属性覆盖掉了
   所以此时输出s2的test属性的话, 值仍然是原来的值
*/

借用构造函数继承

function Father(name, age) {
            this.name = name
            this.age = age
        }
        Father.prototype.fn = function () {
            console.log(123);
        }
​
        function Son(name, age) {
            Father.call(this, name, age)
        }
        // Son.prototype = Father.prototype;  这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化(父类的原型对象会有子类原型对象特有的方法exam,这就是问题)
        // 通过指定父类实例对象方法解决:
        Son.prototype = new Father();
        // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
        Son.prototype.constructor = Son;
        var xm = new Son('小明', 20)
        console.log(xm);
        xm.fn()

this指向问题

调用方法 this指向
普通函数 window
构造函数 实例对象,原型对象里面的方法也指向实例对象
对象方法调用 该方法所属对象
事件绑定方法 绑定事件对象
定时器函数 window
立即执行函数 window
箭头函数 window
//普通函数
function fn1(){
    console.log(this)//window
}
//window.fn1()
fn1()
​
//构造函数
function Person(){
    console.log(this)
}
var xm = new Person()
​
//对象方法调用
var obj = {
    sayHi:function(){
        console.log('对象方法的this:'+this);
    }
}
obj.sayHi();
​
//事件绑定方法
var btn = document.querySelector('button');
        btn.onclick = function() {
            console.log('绑定时间函数的this:' + this);
        };
​
// 5. 定时器函数 this 指向的也是window
        window.setTimeout(function() {
            console.log('定时器的this:' + this);
​
        }, 1000);
        // 6. 立即执行函数 this还是指向window
        (function() {
            console.log('立即执行函数的this' + this);
        })();
​
//箭头函数
// 箭头函数中不绑定this,箭头函数中的this指向是它所定义的位置
 var uname = '张三'
 var obj = {
      uname :'李四',
      fn: function () {
      setTimeout(() => console.log(this.uname), 1000)//this指向李四
            }
        }
 obj.fn()

改变函数内部this指向

改变函数内this指向 js提供了三种方法 call() apply() bind()

//call()
var obj = {
            name: 'andy'
        }
        function fn(a, b) {
            console.log(a + b);
            console.log(this);
        }
        fn(); //此时的this指向的是window
        fn.call(obj, 2, 3); //此时的this指向的是对象obj
        // call 第一个可以调用函数 第二个可以改变函数内的this 指向
        // call 的主要作用可以实现继承
        function Father(uname,age,sex){
            this.uname= uname;
            this.age = age
            this.sex = sex;
        }
        function Son(uname,age,sex){
           Father.call(this,uname,age,sex)
        }
        var son = new Son('小米',18,'男')
        console.log(son);
​
//apply()
   function fn(a, b) {
            console.log(a + b);
            console.log(this);
        }
        var o = {}
        fn() //此时的this指向的是window
        fn.apply(o, [2, 3]) //此时的this指向的是对象o,参数使用数组传递
        // 1. 也是调用函数 第二个可以改变函数内部的this指向
        // 2. 但是他的参数必须是数组(伪数组)
​
//bind()
// 1. 不会调用原来的函数   可以改变原来函数内部的this 指向
// 2. 返回的是原函数改变this之后产生的新函数
// 3. 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向此时用bind
        function fn() {
            console.log(this);
        }
        var o = {
            name: '测试'
        }
        // fn.bind(o)()相当于以下代码
        var fn1 = fn.bind(o)
        fn1()

call、apply、bind三者的异同

  • 共同点 : 都可以改变this指向

  • 不同点:

    • call 和 apply 会调用函数, 并且改变函数内部this指向.

    • call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递

    • bind 不会调用函数, 可以改变函数内部this指向.

  • 应用场景

    1. call 经常做继承.

    2. apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值

    3. bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.

数组的方法

//forEach 替代for循环,缺点不能提前中止
var arr = [1, 2, 3];
        var sum = 0;
        // forEach不能用break提前终止
        arr.forEach(function (value, index, array) {
            console.log(value); //数组每个元素
            console.log(index); //数组每个元素的索引
            console.log(array); //数组
            sum += value;
​
        })
        console.log(sum);
​
//map:映射 
var arr = [2, 3, 4]
var arr1 = arr.map(function (value) {
            return value * value;
        })
console.log(arr1);
​
//filter:过滤
 var arr = [12, 5, 36, 15, 21]
 var newArr = arr.filter(function (value, index, arrat) {
            // return value > 20
            return value % 2 == 0
        })
        console.log(newArr);
​
//some:判断只要有一个满足就是true
var arr = [12, 5, 36, 15, 21]
//返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环
// 遍历数组,判断是否有满足条件的元素,如果有返回true,如果没有返回false
var bool = arr.some(function (value, index, arrat) { 
            return value % 2 == 0
        })
 console.log(bool);
​
//every:只有每一项满足就是true
var arr = [12, 5, 36, 15, 21]
//返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环
// 遍历数组,判断是否有满足条件的元素,如果有返回true,如果没有返回false
var bool = arr.every(function (value, index, arrat) {
      return value % 2 == 0
 })
 console.log(bool);
 
//reduce
 var arr = [2, 3, 4]
var sum = arr.reduce(function (a, b) {
        console.log(a,b);
        return a + b;
        })
console.log(sum);
​
//find
let arr = [{
            id: 1,
            name: '张三'
        }, {
            id: 2,
            name: '李四'
        }, {
            id: 3,
            name: '王五'
        }];
        var obj = arr.find(item => item.id == 2)
        console.log(obj);
        console.log(obj instanceof Object);
        //找数组里面符合条件的值,当数组中元素id等于2的查找出来,注意,只会匹配第一个
        //要查找的条件,写到箭头函数的函数体中
        //find接收的箭头函数的参数是固定的,item,属性值
        //注意这里的属性值item是一个个的对象
        //obj是id为2的item对象
​
//findIndex
 let arr = [{
            id: 1,
            name: '张三'
        }, {
            id: 2,
            name: '李四'
        }, {
            id: 3,
            name: '王五'
        }];
        var obj = arr.findIndex((item, index) => item.id == 2)
        console.log(obj); //1 返回的是索引号
​
//Array.from
var arr = {
            0: 1,
            1: 2,
            2: 3,
            length: 3
        }
        var arr1 = Array.from(arr); //转化成真正的数组
        console.log(arr1);
        console.log(arr1 instanceof Array); //true
​
//扩展运算符...  将数组转为用逗号分隔的参数序列
 let ary = [1, 2, 3];
 ...ary  // 1, 2, 3
 console.log(...ary);    // 1 2 3,相当于下面的代码
 console.log(1,2,3);
​
//扩展运算符可以应用于合并数组
// 方法一 
let ary1 = [1, 2, 3];
let ary2 = [3, 4, 5];
let ary3 = [...ary1, ...ary2];
// 方法二 
ary1.push(...ary2);
//方式三
//concat() 方法用于连接两个或多个数组。
console.log(ary1.concat(ary2));
​
//将伪数组或可遍历对象转换为真正的数组
let oDivs = document.getElementsByTagName('div'); 
oDivs = [...oDivs];

数组方法练习---商品案例

<style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
​
        .box {
            width: 500px;
            height: 400px;
            background-color: pink;
            border: 1px solid black;
            margin: 100px auto;
        }
​
        .bHead {
            width: 100%;
            height: 50px;
            text-align: center;
            border: 2px solid green;
        }
​
        .inText {
            width: 60px;
            height: 20px;
        }
​
        .bBody {
            width: 100%;
            height: 350px;
            text-align: center;
            border: 2px solid green;
        }
​
        .bBody table {
            width: 100%;
            height: 100%;
        }
    </style>
</head>
​
<body>
    <div class="box">
        <div class="bHead">
            价格:
            <input type="text" class="inText" id="star">-<input type="text" class="inText" id="end">
            <button id="pBtn">查询</button>
            品牌:
            <input type="text" class="inText" id="bText">
            <button id="tBtn">查询</button>
        </div>
        <div class="bBody">
            <table border="1" cellspacing=0>
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>类型</th>
                        <th>价格</th>
                    </tr>
                </thead>
                <tbody>
​
                </tbody>
            </table>
        </div>
    </div>
    <script>
        var arr = [{
                id: 1,
                name: '小米',
                price: 3099
            },
            {
                id: 2,
                name: 'oppo',
                price: 1999
            },
            {
                id: 3,
                name: '华为',
                price: 3599
            }
        ]
        var tbody = document.querySelector('tbody');
        var star = document.querySelector('#star');
        var end = document.querySelector('#end');
        var pBtn = document.querySelector('#pBtn');
        var tBtn = document.querySelector('#tBtn');
        var inText = document.querySelector('#bText');
​
        function newArr(myArr) {
            tbody.innerHTML = ''
            myArr.forEach(function (value) {
                var tr = document.createElement('tr');
                tr.innerHTML = '<td>' + value.id + '</td><td>' + value.name + '</td><td>' + value.price +
                    '</td>';
                tbody.appendChild(tr)
            })
        }
        newArr(arr);
        pBtn.addEventListener('click', function () {
            // console.log('123');
​
            var arr1 = arr.filter(function (value) {
                // console.log(value);//返回的是arr数组中的对象
                return value.price >= star.value && value.price <= end.value;
​
            })
            console.log(arr1);
            newArr(arr1);
        })
        tBtn.addEventListener('click', function () {
            // console.log(inText)
            // console.log('123');
            var searchArr = [];
            arr.some(function (value) {
                // console.log(value.name, inText.value)
                if (value.name == inText.value) {
                    searchArr.push(value);
                    console.log(searchArr);
                }
                console.log(searchArr)
                newArr(searchArr);
            })
        })
    </script>
</body>
​

trim()方法去除空格

​
 <input type="text">
    <button>点击</button>
    <script>
        var input = document.querySelector('input');
        var btn = document.querySelector('button');
        btn.addEventListener('click', function () {
            var str = input.value.trim();
            // console.log(str);
            if (str == '') {
                alert('请输入内容')
            } else {
                console.log(str);
                console.log(str.length);
            }
        })
    </script>
​
//正则表达式去除空格
 // 自己实现一个trim
        String.prototype.trim1 = function () {
            return this.replace(/^\s+|\s$/g, "")
        }
        var str = '   123';
        var str1 = ' 456     ';
        console.log(str.trim1().length);

获取对象的属性名和数组的属性值

Object.keys(对象)和Object.values(对象) 返回值是一个数组

var data = {
            id: 1,
            pname: '小米',
            price: 3999
        }
        var arr1 = Object.keys(data);
        var arr2 = Object.values(data);
        console.log(arr1);
        console.log(arr2);

修改或者设置对象中的属性Object.defineProperty

Object.defineProperty(对象,修改或新增的属性名,{
        value:修改或新增的属性的值,
        writable:true/false,
       //如果值为false 不允许修改这个属性值
        enumerable: false,
        //enumerable 如果值为false 则不允许遍历
        configurable: false  //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性
})  
// writable/enumerable/configurable的默认值都为true.

函数的进阶

高阶函数对其他函数进行操作的函数,它接收函数作为参数将函数作为返回值输出

//函数作为参数    
function eat(fn) {
       setTimeout(function () {
         console.log('吃晚饭');
         // 吃完晚饭之后做的事情
         fn();
       }, 2000);
     }
​
     eat(function () {
       console.log('去唱歌');
     });
​
//函数作为返回值输出
// 第一次调用生成随机数,以后每次调用都返回第一次的随机值
​
      function getRandom() {
      var random = parseInt(Math.random() * 10) + 1;
      return function () {
      return random;
     }
      }
​
    var fn = getRandom();
​
     console.log(fn());
     console.log(fn());
     console.log(fn());
​

闭包 在一个作用域中可以访问另一个作用域的变量

    function fn() {
      var n = 10;
      return function () {
        return n;
      }
    }
​
    var f = fn();
    console.log(f());
​
/*闭包特点:延展了函数的作用域范围
fn返回值是一个函数, 这个函数的返回值是n, 因为n将来还需要被使用, 所以fn运行完毕之后不会被系统销毁, 而是等着将来提供n, 所以产生了闭包*/

递归 函数自己调用自己 递归,一般都要写一个结束的条件

var count = 0;
//由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return
        function fn() {
            console.log(123);
            count++;
            if (count == 6) {
                return; // 递归里面必须加退出条件
            }
            fn(); //fn自己调用自己,fn就是递归函数
        }
        fn();
//递归求斐波那契数列
<script>
        function fn(n) {
            if (n === 1 || n === 2) {
                return 1
            }
            return fn(n - 1) + fn(n - 2)
        }
        console.log(fn(10));
</script>

拷贝(浅拷贝和深拷贝)

//浅拷贝
/*
浅拷贝, 对象中的基本数据类型拷贝没问题, 但是如果对象中的复杂数据类型拷贝就有问题, 修改一个值, 都会跟着变化. 如下, 如果拷贝的obj1中修改了c的m, 那么obj2的name会跟着变化
浅拷贝只拷贝对象的第一层属性, 只拷贝了引用
*/
var obj1 = {
            a: 2,
            b: 3,
            c: {
                m: 1,
                n: 2
            }
        }
        // 思路一:用 for ... in 一个属性,赋值给新的对象
        // var obj2 = {}
        // for (var key in obj1) {
        //     obj2[key] = obj1[key]
        // };
        // console.log(obj2);
        // 思路二
        var obj2 = Object.assign({}, obj1)
        obj2.a = 4;
        obj1.c.m = 5; //此时两个对象都受影响
        console.log(obj1);
        console.log(obj2);
    </script>
​
//深拷贝
/*
必须先判断是否是组数, 再判断是否是对象, 因为数组也是对象, 所以反过来的话, 会把所有的数组也当成对象来处理了
*/
var obj = {
            a: 2,
            b: 3,
            c: {
                m: 1,
                n: 2
            },
            d: [2, 3, 4, 5, 6]
        }
var o = {}
function deepCopy(newobj, oldobj) {
    for (var k in oldobj) {
    if (oldobj[k] instanceof Array) {
         newobj[k] = deepCopy([], oldobj[k])
            } else if (oldobj[k] instanceof Object) {
               newobj[k] = deepCopy({}, oldobj[k])
            } else {
                 newobj[k] = oldobj[k]
                }
            };
             return newobj;
        }
        deepCopy(o, obj);
        console.log(o);
        console.log(obj);
​

正则表达式 用于匹配字符串中字符组合的模式

边界符

边界符 说明
^ 表示匹配行首的文本(以谁开始)
$ 表示匹配行尾的文本(以谁结束)

如果 ^和 $ 在一起,表示必须是精确匹配。

var rg = /abc/; // 正则表达式里面不需要加引号 不管是数字型还是字符串型
// /abc/ 只要包含有abc这个字符串返回的都是true
console.log(rg.test('abc'));//true
console.log(rg.test('abcd'));//true
console.log(rg.test('aabcd'));//true
console.log('---------------------------');
var reg = /^abc/;
console.log(reg.test('abc')); // true
console.log(reg.test('abcd')); // true
console.log(reg.test('aabcd')); // false
console.log('---------------------------');
var reg1 = /^abc$/; // 精确匹配 要求必须是 abc字符串才符合规范
console.log(reg1.test('abc')); // true
console.log(reg1.test('abcd')); // false
console.log(reg1.test('aabcd')); // false
console.log(reg1.test('abcabc')); // false

字符类

[]表示有一系列字符可供选择,只要匹配其中一个就可以了

 // 中括号外边的^为边界符,代表从什么开始
        var reg = /^[a,b,c]$/
        console.log(reg.test('a'));
        //中括号里面的^意思为取反
        var reg1 = /^[^a,b,c]$/
        console.log(reg1.test('a'));

量词符

量词 说明
* 重复0次或更多次(*,理解为任意)(0,1,n)
+ 重复1次或更多次(+:代表大于等于1的数。以0为分界线,左边是负数-,右边是正数+)
? 重复0次或1次 (?问号,理解为有没有)
{n} 重复n次
{n,} 重复n次或更多次(没有第二个数字,那就意味着是?,没有上界)
{n,m} 重复n到m次 ({}:区间符号,次数区间)
// 1次或者多次 >=1
        var reg1 = /^a+$/
        // 0次或者多次 >=0
        var reg2 = /^a*$/
        // 0次或者1次 0||1
        var reg3 = /^a?$/
        // 3次
        var reg4 = /^a{3}$/
        // 3-5次
        var reg5 = /^a{3,5}$/
        // 3次以上
        var reg6 = /^a{3,}$/
        // 只能是数字 字母 下划线组成
        var reg7 = /^[a-zA-Z0-9_]*$/
        // 只能是数字 字母 下划线组成,长度6-18
        var reg8 = /^[a-zA-Z0-9_]{6,18}$/

括号总结

1.大括号 量词符. 里面表示重复次数

2.中括号 字符集合。匹配方括号中的任意字符.

3.小括号表示优先级

预定义类

理解:

d:digit ,数字

w:word,单词

s:space,空白,间隔

预定义类 含义
/d 0-9任一数字,相当于[0-9]
/D 0-9数字以外的任意字符,相当于0-9
/w 任意的数字字母以及下划线 [0-9a-zA-Z_]
/W 除去数字字母以及下划线的字符 0-9a-zA-Z_
/s 匹配空格 (回车 tab 空格) 相当于[\t\r\n\v\f]
/S 匹配非空格 字符相当于[6\t\r\n\v\f]

正则替换replace

replace() 方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。

//过滤敏感词汇案例
<textarea name="" id="message"></textarea> <button>提交</button>
<div></div>
<script>
    var text = document.querySelector('textarea');
    var btn = document.querySelector('button');
    var div = document.querySelector('div');
    btn.onclick = function() {
        div.innerHTML = text.value.replace(/激情|gay/g, '**');
    }
</script>

let

特点: 1. let声明的变量只存在于块级作用域 2.不存在变量提升 3.存在暂时性死区

//块级作用域
 if (true) { // 大括号可以形成块级作用域
     let a = 10;
 }
console.log(a) //报错: a is not defined
​
//无变量提升
console.log(a); // a is not defined 
let a = 20;
​
//暂时性死区  利用let声明的变量会绑定在这个块级作用域,不会受外界的影响
 var tmp = 123;
 if (true) { //因为在此块级作用域中,使用了let声明了tmp变量,那么tmp会与此块级作用域进行绑定,形成死区,不受外界影响(无法访问外部的tmp)
     tmp = 'abc';//这里会报错,变量未声明,不能赋值
     let tmp; 
 } 

let经典面试题

此题的关键点在于变量i是全局的,函数执行时输出的都是全局作用域下的i值。
//没有用到let
var arr = [];
 for (var i = 0; i < 2; i++) {
     arr[i] = function () {
         console.log(i); 
     }
 }
 arr[0]();//2
 arr[1]();//2
​
​
//用到let的时候
 let arr = [];
 for (let i = 0; i < 2; i++) {
     arr[i] = function () {
         console.log(i); 
     }
 }
 arr[0]();//0
 arr[1]();//1
/*
- 此题的关键点在于每次循环都会产生一个块级作用域,
- 每个块级作用域中的变量都是不同的,
- 函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的i值.
*/

小结

  • let关键字就是用来声明变量的

  • 使用let关键字声明的变量具有块级作用域

  • 在一个大括号中 使用let关键字声明的变量才具有块级作用域 var关键字是不具备这个特点的

  • 防止循环变量变成全局变量

    • 循环中的i,使用var声明的话是全局变量,但是这样不合理,而使用let就是局部变量

  • 使用let关键字声明的变量没有变量提升

  • 使用let关键字声明的变量具有暂时性死区特性

    • 死区:不受外界影响

const 声明常量,常量就是值(内存地址)不能变化的量

常量:常态的量,值是常态的量,值无法改变

(const:constant常量)

1.存在块级作用域 2.声明的常量必须赋值,且赋值后无法修改 3.不存在变量提升

//存在块级作用域
 if (true) { 
     const a = 10;
 }
console.log(a) // a is not defined
​
//声明的常量必须赋值,且赋值后无法修改
const PI = 3.14;
PI = 100; // Assignment to constant variable. //给常量重新指定值
​
const ary = [100, 200];
ary[0] = 'a';//复杂数据类型内部的值,是可以改变的
ary[1] = 'b';
console.log(ary); // ['a', 'b']; 
ary = ['a', 'b']; // Assignment to constant variable.//但是复杂数据类型变量本身无法改变

小结

  • const声明的变量是一个常量

  • 既然是常量不能重新进行赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址值

    • 复杂数据类型变量中存储的是地址值

  • 声明 const时候必须要给定初始值(因为没有办法重新赋值,所以在声明时必须有初始值

    var let const
    函数级作用域 块级作用域 块级作用域
    变量提升 不存在变量提升 不存在变量提升
    值可更改 值可更改 值不可更改

    解构赋值 可以让我们更快捷的从数组或对象中提取值,然后对变量赋值

    //数组
     let [a, b, c] = [1, 2, 3];
     console.log(a)//1
     console.log(b)//2
     console.log(c)//3
    //如果解构不成功,变量的值为undefined
    ​
    ​
    //对象
    let { name, age } = { name: 'zhangsan', age: 20 };
    console.log(name); // 'zhangsan' 
    console.log(age); // 20
    ​
    //注意这里的name仅仅是用于属性匹配,不在是变量,myName才是变量
    let {name: myName, age: myAge} = { name: 'zhangsan', age: 20 };
    console.log(myName); // 'zhangsan' 
    console.log(name);//无法获取到zhangsan
    console.log(myAge); // 20
    //如果变量不想与对象的属性名保持一致,那么采用第二种方式解构

    小结

    • 解构赋值就是把数据结构分解,然后给变量进行赋值

    • 如果解构不成功,变量跟数值个数不匹配的时候,变量的值为undefined

    • 数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开

    • 利用解构赋值能够让我们方便的去取对象中的属性跟方法

    • 补充:

      //不管是数组还是对象在去处理结构赋值时,都是通过key匹配变量,然后进行赋值.
       let [a, b, c] = [0:1, 1:2, 2:3];
      let { name, age } = { name: 'zhangsan', age: 20 };

箭头函数

  • 箭头函数中不绑定this,箭头函数中的this指向是它所定义的位置

    • 理解:箭头函数不绑定this,箭头函数没有自己的this关键字,如果在箭头函数中使用this,this关键字将指向箭头函数定义位置中的this

    • 再理解:理解为当前箭头函数作用域中没有this,要向上一级作用域查找

  • 箭头函数的优点在于解决了this执行环境所造成的一些问题。

    • 比如:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括setTimeout和setInterval中使用this所造成的问题

//语法
/*函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
如果形参只有一个,可以省略小括号
箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
*/
() => {} //():代表是函数; =>:代表指向,指向哪一个代码块;{}:代码块,函数体
const fn = () => {}//代表把一个函数赋值给fn
​
//示例
const obj = { name: '张三'} 
 function fn () { 
     console.log(this);//this 指向 是obj对象
     return () => { 
         console.log(this);
         //this 指向 的是箭头函数定义区域的this,那么这个箭头函数定义在fn里面,而这个fn指向是的obj对象,所以这个this也指向是obj对象
         //理解为当前箭头函数作用域中没有this,要向上一级作用域查找
     } 
 } 
 const resFn = fn.call(obj); 
 resFn();
​

string扩展方法

模板字符串中可以写代码,html代码(可以识别空格和换行等格式),js代码(写在${})

1.模板字符串中可以解析变量 2.模板字符串中可以换行 3.在模板字符串中可以调用函数 4.同样是通过${}来调用

​
let obj = {
            uname: '张三',
            age: 18
        }
模板字符串中,通过${变量名}的方式来获取变量值
var str = `你的名字是${obj.uname},你的年龄是${obj.age},${Math.random()}`
 console.log(str);//你的名字是张三,你的年龄是18,0.8866876958290935(随机数)
var str1 = `<div> <h3>${obj.uname}</h3></div>`
console.log(str1);//<div> <h3>张三</h3></div>

实例方法

startsWith():表示参数字符串是否在原字符串的头部,返回布尔值

endsWith():表示参数字符串是否在原字符串的尾部,返回布尔值

var str = 'abc.jpg';
        var reg = /jpg$/
        if (reg.test(str)) {
            console.log(1);
        } else {
            console.log(2);
        }
        console.log(str.endsWith('jpg')); //true 以什么结尾
        console.log(str.startsWith('abc')); //true 以什么开头

repeat():表示将原字符串重复n次,返回一个新字符串

'x'.repeat(3)      // "xxx" 
'hello'.repeat(2)  // "hellohello"

set数据结构

实例方法:

  • add(value):添加某个值,返回 Set 结构本身

  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功

  • has(value):返回一个布尔值,表示该值是否为 Set 的成员

  • clear():清除所有成员,没有返回值

     const s = new Set();
     s.add(1).add(2).add(3); // 向 set 结构中添加值 
     s.delete(2)             // 删除 set 结构中的2值   
     s.has(1)                // 表示 set 结构中是否有1这个值 返回布尔值 
     s.clear()               // 清除 set 结构中的所有值
     //注意:删除的是元素的值,不是代表的索引

    遍历

    Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。

     const s = new Set();
     s.add(1).add(2).add(3); 
    s.forEach(value => console.log(value))

猜你喜欢

转载自blog.csdn.net/qq_34194159/article/details/106902757