Object.defineProperty
- GET
用法:回调函数,根据其他相关的属性,动态计算得到当前属性值
- SET
用法:回调函数,监视当前属性值的变化,更新其他相关的属性值
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
语法:Object.defineProperty(obj, prop, descriptor)
模版解析
什么是模版?
答:HTML
中嵌套js
代码
js
代码以什么样的形式存在于页面中?
答:两种语法。指令属性和大括号表达式。
变量和函数提升
变量声明提升:通过var
定义(声明)的变量,在定义语句之前就可以访问到。
值:
undefined
函数声明提升:通过function
声明的函数,在声明之前就可以直接调用
值:函数定义(对象),就是可以直接调用。
注意:函数提升必须是通过function
声明的函数,而图中的fn3
是通过变量定义的函数,它会先进行变量提升,所以fn3()
就会调用失败。
数据类型
- 基本(值)类型
String
:任意字符串Number
:任意的数字Boolean
:true
或者false
undefined
:undefined
null
:null
- 对象(引用)类型
Object
:任意对象Function
:一种特别的对象(可以执行)Array
:一种特别的对象(里面的数据都有数值下标,内部数据是有序的)
一个函数和一个一般的对象区别在哪?
答:对象是用来存储数据的;函数是可以执行的对象,它的内部存储的是一段可以执行的代码。
判断
typeof
typeof
返回数据类型的字符串表达式,且字符串都是小写。- 可以判断:
undefined
/数值/字符串/布尔值/function
- 不可判断:
null
和object
、object
和Array
。因为都返回同一个类型。
instanceof
:- 用来判断对象的具体类型
A instanceof B
:A
是不是B
的实例,所以B
是一个构造函数。
===
- 只可以判断
undefine
和null
。因为undefine
和null
这两个类型只有一个值。 ===
和==
的区别:==
会做数据转换,===
不会,它会判断数据是否完全相等。
- 只可以判断
var a;
console.log(a);//undefined
console.log(typeof a);//"undefined"
console.log(typeof a === "undefined");//true
console.log(a === undefined);//true
//所以,typeof 和 === 都可以判断 undefined
typeof
返回的字符串都是小写:
var b ="ben";
console.log(typeof b === "string");//true
console.log(typeof b === "String");//false
typeof
判断数值、字符串、布尔值
var a = 3;
console.log(typeof a === "number");//true
var a = "3";
console.log(typeof a === "string");//true
var a = true;
console.log(typeof a === "bollean");//true
===
判断null
和undefined
:
var c = null;
console.log(typeof c);//"object"
console.log(a === null);//true
var d;
console.log(d === undefined);//true
instance
:用来判断对象的具体类型
var b1 = {
b2: [1, 'abc', console.log],
b3: function () {
console.log('b3')
}
}
console.log(b1 instanceof Object, b1 instanceof Array) // true false
console.log(b1.b2 instanceof Array, b1.b2 instanceof Object) // true true
console.log(b1.b3 instanceof Function, b1.b3 instanceof Object) // true true
补充:
typeof
还可以判断function
类型
console.log(typeof b1.b3==='function') // true
console.log(typeof b1.b2[2]==='function') //true
补充:
typeof
不可以判断类型object
和array
var a = [1,2,3];
console.log(typeof a);//object
数据类型的问题
undefined与null的区别?
- undefined代表定义未赋值
- nulll定义并赋值了, 只是值为null
什么时候给变量赋值为null呢?
- 初始赋值, 表明变量将要赋值为对象。因为
null
也是个对象。 - 结束前, 让对象成为垃圾对象(被垃圾回机制回收,这个垃圾回收机制在浏览器中)
严格区别变量类型与数据类型?
数据的类型
- 基本类型
- 对象类型
变量的类型(变量内存值的类型,因为js
是个弱类型的语言,通过var
定义的变量,本身是没有类型的,而有时候判断变量的类型,其实是判断变量值的类型。) - 基本类型: 保存就是基本类型的数据
- 引用类型: 保存的是地址值
什么是实例?
// 实例: 实例对象
// 类型: 类型对象
function Person (name, age) {
// 构造函数 类型
this.name = name
this.age = age
}
var p = new Person('tom', 12) // 根据类型创建的实例对象
数组的解构
var arr = ['张三','李四','王武']
const [name1, name2, name3] = arr
对象的几种创建模式
Object构造函数模式
实现:先创建空Object对象, 再动态添加属性/方法
代码:
// 先创建空Object对象
var p = new Object()
p = {
}
// 再动态添加属性/方法
p.name = 'Tom'
p.age = 12
p.setName = function (name) {
this.name = name
}
适用场景: 起始时不确定对象内部数据
缺点:语句太多
对象字面量模式
实现:使用{}创建对象, 同时指定属性/方法
代码:
var p = {
name: 'Tom',
age: 12,
setName: function (name) {
this.name = name
}
}
适用场景: 起始时对象内部数据是确定的
缺点: 如果创建多个对象, 有重复代码
var p2 = {
//如果创建多个对象代码很重复
name: 'Bob',
age: 13,
setName: function (name) {
this.name = name
}
}
工厂模式
实现:通过工厂函数动态创建对象并返回
代码:
function createPerson(name, age) {
//返回一个对象的函数===>工厂函数
var obj = {
name: name,
age: age,
setName: function (name) {
this.name = name
}
}
return obj
}
// 创建2个人
var p1 = createPerson('Tom', 12)
var p2 = createPerson('Bob', 13)
// p1/p2是Object类型
适用场景: 需要创建多个对象
缺点: 对象没有一个具体的类型, 都是Object类型
自定义构造函数模式
实现:自定义构造函数, 通过new创建对象
代码:
//定义类型
function Person(name, age) {
this.name = name
this.age = age
this.setName = function (name) {
this.name = name
}
}
var p1 = new Person('Tom', 12)
p1.setName('Jack')
适用场景: 需要创建多个类型确定的对象
缺点: 每个对象都有相同的数据, 浪费内存
构造函数+原型的组合模式
实现:自定义构造函数, 属性在函数中初始化, 方法添加到原型上
代码:
function Person(name, age) {
//在构造函数中只初始化一般函数
this.name = name
this.age = age
}
Person.prototype.setName = function (name) {
this.name = name
}
var p1 = new Person('Tom', 23)
var p2 = new Person('Jack', 24)
console.log(p1, p2)
适用场景: 需要创建多个类型确定的对象
ES5相关扩展
JSON
JSON.stringify(obj/arr)
:把原生js
对象(数组)转换为json
对象(数组)JSON.parse(json)
:把json
对象(数组)转换为js
对象(数组)
注意:JSON
对象和JSON
数组,而平时所说的JSON
字符串是JSON
对象和JSON
数组的数据类型,即JSON
对象和JSON
数组都是string
类型
分析:
var obj = {
name: 'kobe',
age: 39
};
//把原生的 js 对象转化为 JSON 对象
objJSON = JSON.stringify(obj)
console.log(typeof objJSON)//string
console.log(objJSON)// {"name":"kobe","age":39}
//把JSON对象转化为原生 js 对象
obj = JSON.parse(objJSON)
console.log(obj)//object
var str = "我是字符串"
console.log(str)//我是字符串
//把原生的 js 字符串转化为 JSON 字符串
strJSON = JSON.stringify(str)
console.log(strJSON)//"我是字符串"
var arr = [1,2,3]
//把原生 js 数组转化为 JSON 数组
arrJSON = JSON.stringify(arr)
console.log(typeof arrJSON)//string
Object扩展
-
Object.create(prototype, [descriptors])
作用:以指定对象为原型创建新的对象;为新的对象指定新的属性, 并对属性进行描述
- value : 指定值
- writable : 标识当前属性值是否是可修改的, 默认为false
- configurable: 标识当前属性是否可以被删除 默认为false
- enumerable: 标识当前属性是否能用for in 枚举 默认为false
代码:
var obj1 ={ name: 'kobe', age: 18 } var obj2 = { } obj2 = Object.create(obj1, { sex:{ value: '男', writable: true, configurable: false, enumerable: true } })
-
Object.defineProperties(object, descriptors)
作用:为指定对象定义扩展多个属性
- get :用来获取当前属性值得回调函数
- set :监听当前属性值得触发的回调函数,并且实参即为修改后的值
代码:
var obj2 = { firstName: 'curry', lastName: 'stephen' }; Object.defineProperties(obj2, { fullName: { get: function() { //获取扩展属性的值。获取扩展属性的值的时候,get方法自动调用 return this.firstName + '-' + this.lastName }, set: function(data) { //监听扩展属性,当扩展属性发送变化的时候自动调用。自动调用后,会将变化的值作为实参注入到set函数 var names = data.split('-'); this.firstName = names[0]; this.lastName = names[1]; } } }); console.log(obj2.fullName);//curry-stephen obj2.firstName = 'tim'; obj2.lastName = 'duncan'; console.log(obj2.fullName);//tim-duncan obj2.fullName = 'kobe-bryant'; console.log(obj2.fullName);//kobe-bryant
Function的扩展
Function.prototype.bind(obj)
:将函数内的this
绑定为obj
, 并将函数返回
问:区别
bind()
与call()
和apply()
?
-
都能指定函数中的this
-
call()
/apply()
是立即调用函数 -
bind()
:绑定完this
不会立即调用当前的函数,而是将函数返回,所以通常用bind()
来指定回调函数的this
,因为回调函数最终什么时候调用不是我们能决定的,再有就是,回调函数也不是一上来就调用的 -
bind()
传递参数的方式和call()
一样function fun(age) { this.name = 'kobe'; this.age = age; } var obj = { }; fun.bind(obj, 12)(); console.log(obj.name, obj.age);//kobe 12
-
call()
和apply()
的区别:在不传参数的情况下,二者使用的方式是一样的;如果传递参数了,则call()
里面传递的参数可以一个一个传,但是apply()
里面传递的参数要放到一个数组里面传
let和const
let
:与var类似, 用于声明一个变量
特点:在块作用域内有效;不能重复声明;不会预处理, 不存在变量提升,预处理的过程就是变量提升
应用:循环遍历加监听,因为let
有自己的块级作用域
const
:定义一个常量
特点:不能修改;其它特点同let
应用:保存不用改变的数据
解构赋值
理解:从对象或数组中提取数据, 并赋值给变量(多个)
-
对象的解构赋值
let obj = { name : 'kobe', age : 39}; let { name, age} = obj; console.log(name,age);
-
数组的解构赋值
let arr = [1, 'abc', 23, true]; let [a, b, c, d] = arr; let [, , a b ] = arr console.log(a, b, c, d);//1, 'abc', 23, true
-
用途:给多个形参赋值
箭头函数
作用:定义匿名函数
基本语法:
- 没有参数: () => console.log(‘xxxx’)
- 一个参数: i => i+2
- 大于一个参数: (i,j) => i+j
- 函数体不用大括号: 默认返回结果
- 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
使用场景:多用来定义回调函数
箭头函数的特点:
- 简洁
- 箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
- 扩展理解: 箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。
深度克隆
拷贝数据:
- 基本数据类型:拷贝后会生成一份新的数据,修改拷贝以后的数据不会影响原数据
- 对象/数组:拷贝后不会生成新的数据,因为拷贝的是引用,所以修改拷贝以后的数据会影响原来的数据
拷贝数据的方法:
- 直接赋值给一个变量。(浅拷贝)
Object.assign()
。(浅拷贝)Array.prototype.concat()
。(浅拷贝)Array.prototype.slice()
。(浅拷贝)JSON.parse(JSON.stringify())
。(深拷贝/深克隆,但是这种方法,拷贝的数据里面不能有函数,因为JSON
的这些方法处理不了函数。)
浅拷贝(对象/数组)的特点:拷贝的是引用,所以修改拷贝以后的数据会影响原数据,使得原数据不安全。
深拷贝(深度克隆)的特点:拷贝的时候会生成新数据,修改拷贝以后的数据不会影响原数据。
思考:如何实现深度拷贝(深度克隆)?
答:因为拷贝的数据里面有对象/数组,所以导致了浅拷贝,所以解决方法就是让数据里面没有对象/数组。那如果数据里面就是有对象/数组,该怎么办呢?那就继续遍历对象/数组,拿到其中的每一个值,一直到拿到的都是基本数据类型的时候,再去复制,也就没有浅拷贝,都是深度拷贝了。
准备知识:
判断数据类型:Object.prototype.toString.call(obj)
截取数据:Object.prototype.toString.call(obj).slice(8,-1)
for in
循环:枚举对象时,输出的是属性名;枚举数组时,输出的是下标
定义检测数据类型的功能函数:
function checkedType(target){
return Object.prototype.toString.call(target).slice(8,-1)
}
实现深度拷贝(更多针对的是数组或者对象):
function deepClone(target) {
let result, targetType = checkedType(target);
if(targetType === 'Object'){
result = {
};
}else if(targetType === 'Array'){
result = [];
}else {
return targetType; // 如果是其他数据类型不复制,直接将数据返回
}
// 遍历目标数据
for(let i in targetType){
//获取遍历数据结构的每一项值
let value = target[i];
//判断目标结构里的每一项值是否存在对象/数组
if(checkedType(value) === "Object" || checkedType(value) === 'Array'){
//继续遍历获取到的 value 值
result[i] = deepClone(value);
}else {
//获取到的 value 值是基本的数据类型或者函数
result[i] = value;
}
}
return result;
}