评论

(一) js高级程序设计 中转笔记

js

0. 变量声明,let , var , const 的区别,隐式创建全局变量

var var 有变量提升(先使用,后声明)可以跨块访问,不能跨函数访问,

let let 声明的变量,作用域是块级作用域,不能重复声明变量

const (constant 常数 固定的 不变的)必须有初始化值(const a = 0;);值不可以修改(变量的指针不可以修改)

隐式创建全局变量

a = 0;这样全局都可以访问 a = 0;

创建全局变量

window.a = 0

1. typeof 返回哪些数据类型

boolean string number object function
console.log(typeof true) //boolean
console.log(typeof "a") //string
console.log(typeof 100) //number
console.log(typeof null) //object
console.log(typeof undefined) //undefined
console.log(typeof []) //object
console.log(typeof function(){}}) //function
console.log(typeof {}) //object

2.列举出部分强制类型转换和隐式类型转换

强制类型转换

parseInt()
parseFloat()
Number()

隐式类型准换

if()   // 自动调用Boolean()
==
!      // 自动调用Boolean() 然后取非

Boolean() 转为boolean类型:

转为string类型 参数直接 + " " 也可以转为字符串

number  boolean object 都有toString()方法
true 转为"true" false转为"false"
null undefined 没有 toString()方法
在不知道参数有没有toString()方法时 用String()转为字符串
String({})  // "[object Object]"
String(function(){a:0;function a(){console.log(a)}})   // "function(){a:0;function a(){console.log(a)}}"String()时,该参数有toString()就返回toString()的值,否则 null返回"null" ,undefined返回"undefined"
String([222,333,4444,555,"222222222"]) // "222,333,4444,555,222222222"

转为number

Number()   parseInt()   parseFloat()
parseInt() parseFloat()  专门用于把字符串转为数值
    parseInt("www")  //NaN
    parseInt({})  //Nan
    parseInt("1.11")  //1
    parseFloat("1.11")  //1.11
Number()可以将任何类型转为数值,规则如下:
    boolean值  true1  false0
    null0
   对象/空对象 Number({}) ->  NaN 
   undefinedNaN
   如果是字符串:
       纯数字字符串,转为对应的整数或者浮点数
       空字符串 "" 转为0
       其他字符串 如 "hello world" 转为NaN
   如果是对象 先调用对象的valueOf,再按前面的规则转换   

== 转换规则:

  1. 如果其中一个是布尔值,则先将布尔值转为数值 false -> 0 true -> 1
  2. 如果一个是字符串,一个是数值,则将字符串转为数值 再 比较相等
  3. 如果一个是对象,先调用valueOf得到这个对象的基本类型值,再按前面的规则比较
    另外:
  4. null 和 undefined 是相等的
  5. 比较null 和undefined前,不能将null和undefined转为其他值 ( 因为null和ndefined原型链没有valueOf()函数 )
  6. 只要有一个是NaN,就返回false ,NaN不等于NaN
  7. 两个对象判断相等,只要看这两个对象是不是指向同一个对象,如果不是同一个对象,即使两个对象的属性与值完全一样,也不相等

3. split() 与join()的区别

split()将字符串分割为数组
join()将数组转为字符串

4.pop push unshift shift的区别

pop() 尾部移除
push() 尾部添加
shift() 头部移除
unshift() 头部添加


数组相关

判断数组

Object.ptototype.toString.call([])  // '[Object Array]'
Array.isArray([])  //true
[] instanceOf Array  // true

数组增删改查

栈方法 后进先出 LIFO(last in first out)
pop() ; push()
队列方法 先进先出 FIFO(first in first out)
shift() unshift()

数组转换

由于每个对象都有toString() toLocaleString() valueOf() ,所以数组也有

[1,2,3].toString() // "1,2,3"
[1,2,3].valueOf() // [1,2,3]

split() join()

重排列方法

reverse()   // 改变原数组顺序,返回改变后的数组
sort()      // 接受一个比较函数 返回赋值表示第一个参数在第二个参数之前  。。。 会改变原数组,返回值是改变后的数组

操作方法

concat()  // 基于当前数组创建一个副本,然后将接收到的参数放到这个副本的末尾,然后返回新的数组
slice(start , end) 
// 两个参数:返回开始-结束(不包括结束位置的)的数组   一个参数 : 从该参数到最后    不会对原参数造成影响
//  如果有负数,则用数组长度加上该数确定位置

splice()
// splice 会对原来数组造成影响,返回剪切的值
// 接受三个参数或更多 起始位置 剪切数量 替换的值
// 删除: 两个参数
// 替换: 起始位置 替换数量 替换的值
// 插入: 起始位置 0 插入的值
let arr = [1,2,3,4,5,6]
let arr2 = arr.splice(0,2 , '22' , '22')
console.log(arr)    // ['22' , '22' , 3,4,5,6]
console.log(arr2)   //  [1,2]

位置方法

indexOf() 
// 接受两个参数 要查找的项和起点位置索引值  查找时相当于 ===    
// 没查到返回-1 找到返回索引值
lastIndexOf()
// 从后往前查

迭代方法

every()  // 对数组中每一项运行给定函数 如果每一项返回true,则返回true
filter()  // 对数组中每一项运行给定函数,返回每一项返回true组成的数组
forEach() //对数组中每一项运行给定函数,没有返回值
map()     // 对数组中每一项运行给定数组,返回每一项结果组成的数组
some()     // 对数组中每一项运行给定数组,只要有一项返回true,就返回true
reduce((pre,curr,index,arr) => {})  // 迭代所有数组,返回固定的值。第一次迭代时,pre为arr[0] curr为arr[1],第二次时pre为第一次函数返回值,curr为arr[2]
reduceRight() // 与reduce一样,迭代方向从后往前

5.事件绑定和普通事件有什么区别

8.call , apply ,bind的区别

call apply bind都有改变this指向的作用 ,并且第一个参数都是目标this对象。
call apply用法一样,只是传参有区别 call第一个参数之后的参数是一个一个传,apply第一个参数是传一个数组
bind传参与call一样,但返回一个函数

let Animal = {
    name: '动物',
    say(dad , mom){
        console.log("my name is " + this.name)
        console.log("my dad " + dad)
        console.log("my mom " + mom)
    }
}
let Dog = {
    name: 'Dog'
}
Animal.say.call(Dog , 'dadName' , 'momName')
Animal.say.apply(Dog , ['dadName' , 'momName'])
let d = Animal.say.bind(Dog , "dadName" , "momName")
d();

实际应用
Object.prototype.toString.call()

9.b继承a的方法

11.添加 删除 替换 插入到某个结点的方法

12.js的本地对象,内置对象和宿主对象

14 == 和 === 的区别

== 会自动转换类型,再进行比较

=== 既比较类型,也比较值

15. js的同源策略

17.判断数据类型的几种方式 , js怎么判断数组

typeof
可以判断基本数据类型 判断函数 判断undefined 无法判断数组 null 对象 ,无法判断自定义的引用变量
instanceof
可以判断引用变量,判断函数,判断数组,判断自定义的引用变量
Object.prototype.toString.call()
可以判断基本数据类型和引用数据类型,但无法判断自定义数据类型

判断数组
[] instanceof Array
Array.isArray([])

对象

1. 理解对象

let person = new Object();
person.name = 'wu';

//  或者
let person2 = {
    name: 'wu'
}

1.1 属性类型

  1. 数据属性
let person = {
name: 'wujie'
}    
Object.defineProperty(person ,'name' ,{
configurable: true , // 默认为true,表示能否删除修改属性的 特性 ,或者把属性改为访问器属性
enumerable: true ,   // 默认为true,表示能否通过for-in循环访问属性
writable: false,      // 表示能否修改属性的值
value: 'wujie'       // 包含属性的值,读取属性的时候,从这里读,改属性的时候,在这里改。 默认为undefined
})
  1. 访问器属性
// 访问器属性
// 访问器属性不包含具体值,它包含set和get函数(这两个函数不是必需的)
let man = {
_name: ''
}
Object.defineProperty(man , 'name' , {
    // 注意name 与 _name
    // 这里是重写name的get set ,
    // 如果重写 _name 的话,获取_name的值时 相当于自己调自己,会栈溢出
    // 表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,
    // 或能否把属性修改为数据属性,对于直接在对象上定义的属性,默认为true
    configurable: true,  
    enumerable: true ,  // 表示能否通过for-in循环属性,对于直接在对象上定义的属性,默认为true 
    get: function(){
        return this._name; 
    },
    set: function(newValue){
        this._name = newValue
    }
})

1.2 定义多个属性

// 定义多个属性
let woman = {
  _age: 0
}
Object.defineProperties(woman , {
    name: {
        value: 'woman'
    },
    age: {
        get: function(){
            return this._age
        },
        set: function(newValue){
            console.log(newValue)
            this._age = newValue, 
            this.year = newValue + 2020
        }
    },
    year: {
        value: 0
    }
})
woman.age = 20;

1.3读取属性特性

 //读取属性特性
let dog = {
    name: 'dog'
}
let d = Object.getOwnPropertyDescriptor(dog , 'name')
console.log(d.value)
console.log(d.configurable)
console.log(d.enumerable)
console.log(d.writable)

双向绑定的实现

<input type="text" oninput="changeData(this.value)">
// 双向绑定的实现
 let textHTML = document.getElementById("text")
 let data = { 
   _name: ''
 }
 function changeData(e){
    data.name = e;
    console.log(data.name)
 }
 Object.defineProperty(data , 'name' , {
   set: function(newValue){
     this._name = newValue
     textHTML.innerHTML = name
   },
   get: function(){
      return this._name;
   }
 })

2.创建对象

2.1 工厂模式

// 用一个函数构建包含所有参数的对象 ,用函数来封装以特定接口创建对象的细节
// 缺点是无法解决对象识别的问题
function createPerson(name , age ){
    let o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        console.log(this.name)
    }
    return o;
}
let person6 = createPerson('wu' , 10)
person6.sayName()

2.2 构造函数模式

// 2.2 构造函数模式
function Student(name , age ){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        console.log(this.name)
    }
}
let stu = new Student('wu' , 10);
// 要创建Student实例,必须使用new操作符 ,以这种方式调用构造函数会经历一下四个步骤
    // 1. 创建一个新对象
    // 2. 将构造函数的作用域赋给新对象(this指向新对象)
    // 3. 执行构造函数中的代码 
    // 4. 返回新对象
stu.sayName()
console.log(stu.constructor) // stu实例constructor指向构造函数Student,但判断实例类型最好使用instanceOf
// 1. 将构造函数当作普通函数
    // 构造函数与普通函数的区别只在于调用方式不同。用new调用就是构造函数
Student('ww' , 90) // 在全局作用域中调用函数时,函数里的this指向window
window.sayName()
console.log(window.age)
// 2. 构造函数的缺点
    // 每个方法都要在每个实例上重新创一遍 例如: 每个student实例中的sayName()都不是同一个Funtion的实例
    // 如果把方法单独写在外面,那么这个自定义的引用类型就毫无封装的意义了

2.3 原型模式

// 1 .原型模式
    // 只要创建了新函数,就会根据一定规则创建一个prototype,指向这个函数的原型对象
    // 默认情况下,所有原型对象都会获得一个constructor属性,这个属性包含指向prototype所在函数的指针
    // 代码读取某个对象的属性时,会现在实例中找,实例中没有才回到原型中找
    // 可以通过实例访问保存在原型中的值,但不能通过实例重写原型中的值 
function Animal(){}
Animal.prototype.name = 'animal';
let dog = new Animal()

console.log(Object.getPrototypeOf(dog))
dog.name = '333'; console.log(dog.name) // 修改后的值
delete dog.name;  console.log(dog.name) // delete操作符 删除实例中的属性  这时name是原型中的值
dog.name = null; console.log(dog.name) // 即使null也不会修改到原型中的值
let dog2 = new Animal()
console.log(dog2.name) // 原型的值

// 检测属性是在原型中还是实例中 属性在实例中返回true
console.log(dog.hasOwnProperty('name'))  // true
console.log(dog.hasOwnProperty('age'))   // false

// 2. in操作符 只要原型或者实例中存在某个属性,就返回true
dog2.age = 2;
console.log('age' in dog2);
// 所以可以结合hasOwnProperty和 in判断属性到底是存在原型中还是实例中

//3. 简单的原型写法 这样本质上是完全重写了Person的原型对象,因此prototype.constructor也只向的是Object
function Person(){}
Person.prototype = {
    name: 'wu',
    sayName: function(){
        console.log(this.name)
    }
}
let per = new Person()
console.log(Person.prototype.constructor)  // Object
// 但是用instanceOf测试 对Object和Person仍返回true
console.log(per instanceof Person) // true
console.log(per instanceof Object) // true

// 如果constructor的指向真的很重要,可以将他设为特定的值
Person.prototype = {
    constructor: Person,
    name: 'wu',
    sayName: function(){
        console.log(this.name)
    }
}
console.log(Person.prototype.constructor)  // Person


//4. 原型的动态性
// 可以先创建实例,再改变原型,这时仍可以获取原型的属性
function Person(){}
let friend = new Person(); //先创建的实例,这时候无论原型还是实例都没有任何方法
Person.prototype.name = 'wujie';
Person.prototype.sayName = function(){
    console.log(this.name)
}
friend.sayName()  //仍然可以调用
// 但不能直接重写原型对象 , 因为实例friend2保存的只是一个指向原型的指针,重写原型时,相当于切断了实例friend2与原型的联系
// 如
let friend2 = new Person()
Person.prototype = {
    age: 10,
    sayAge: function(){
        console.log(this.age)
    }
}
friend2.sayAge()  //error friend2.sayAge is not a function
// 5. 原生对象的原型


// 6. 原型对象的问题
    // 省略了为构造函数传递初始化参数这一环节,所有实例在默认情况下都取得相同的属性
    // 对与引用类型来说,共享的属性就容易造成问题
function Person(){}
Person.prototype.friends = ['jobs' , 'gates'];
let person1 = new Person();
console.log(person1.friends)
let person2 = new Person();
person1.friends.push('july');
// 由于friends存在于prototype而非person1中,所以对friends的修改通过person2也可以看出来
console.log(person1.friends)
console.log(person2.friends)

2.4 组合使用原型模式和构造函数模式

// 2.4 组合使用原型模式和构造函数模式
  // 实例的属性用构造函数模式定义,方法或需要共享的属性用原型模式定义
function Person (name , age , friends){
    this.name = name;
    this.age = age;
    this.friends = friends;
}
Person.prototype.sayName = function(){
    console.log(this.name)
}
let person1 = new Person('wu' , 20 , ['jobs' , 'gates'])
let person2 = new Person('shen' , 21 , ['jack' , 'rose'])
console.log(person1.friends)
console.log(person2.friends)
person1.friends.push('new')
console.log(person1.friends)
console.log(person2.friends)

2.5 动态原型模式

//2.5 动态原型模式
    // 把所有功能都放在构造函数里面,通过在构造函数里初始化原型,同时保持构造函数和原型模式的优点
function Person(age ,name){
    this.age = age;
    this.name = name;
    if(typeof this.sayName != 'function'){ // 初次调用构造函数时才会执行 多个属性只要检查一个即可
        console.log(this)
        Person.prototype.sayName = function(){
            console.log(this.name)
        }
        Person.prototype.sayAge = function(){
            console.log(this.age)
        }
    }
}
let person1 = new Person('wu' , 12)
let person2 = new Person('wang' , 12)

2.6 寄生函数构造模式

在上述方法都不适用的情况下,可以使用寄生构造函数模式。
创建一个函数,这个函数封装了创建对象的过程,然后返回一个对象。

function Person (name , age){
    let o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function(){
        console.log(this.name)
    }
    return o;
}
let p = new Person('wu' , 10);
console.log(p.age)
p.sayName()

2.7 稳妥函数构造模式

与寄生构造函数类似,但有两点不同

  1. 新创建对象的实例方法不使用this
  2. 不适用new操作符调用构造函数
function Person(name){
    let o = new Object();
    o.sayName = function(){
        console.log(name) // 不使用this
    }
    return o;
}
let p = Person('wu'); // 不使用 new
p.sayName()
最后一次编辑于  2020-05-25  
点赞 1
收藏
评论

2 个评论

  • 刘勇
    刘勇
    2020-05-21

    2020-05-21
    赞同 1
    回复 1
    • 刘勇
      刘勇
      2020-05-21
      错了
      2020-05-21
      回复
  • 郑钱花
    郑钱花
    2020-05-21

    这文章哪拷的,涨经验不错

    2020-05-21
    赞同
    回复
登录 后发表内容