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()
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值 true为1 false为0
null为0
对象/空对象 Number({}) -> NaN
undefined为NaN
如果是字符串:
纯数字字符串,转为对应的整数或者浮点数
空字符串 "" 转为0
其他字符串 如 "hello world" 转为NaN
如果是对象 先调用对象的valueOf,再按前面的规则转换
== 转换规则:
- 如果其中一个是布尔值,则先将布尔值转为数值 false -> 0 true -> 1
- 如果一个是字符串,一个是数值,则将字符串转为数值 再 比较相等
- 如果一个是对象,先调用valueOf得到这个对象的基本类型值,再按前面的规则比较
另外: - null 和 undefined 是相等的
- 比较null 和undefined前,不能将null和undefined转为其他值 ( 因为null和ndefined原型链没有valueOf()函数 )
- 只要有一个是NaN,就返回false ,NaN不等于NaN
- 两个对象判断相等,只要看这两个对象是不是指向同一个对象,如果不是同一个对象,即使两个对象的属性与值完全一样,也不相等
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 属性类型
- 数据属性
let person = {
name: 'wujie'
}
Object.defineProperty(person ,'name' ,{
configurable: true , // 默认为true,表示能否删除修改属性的 特性 ,或者把属性改为访问器属性
enumerable: true , // 默认为true,表示能否通过for-in循环访问属性
writable: false, // 表示能否修改属性的值
value: 'wujie' // 包含属性的值,读取属性的时候,从这里读,改属性的时候,在这里改。 默认为undefined
})
- 访问器属性
// 访问器属性
// 访问器属性不包含具体值,它包含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)