实现一个new
首先说说new操作符的作用
new
操作符会返回一个对象,所以我们需要在内部创建一个对象。- 这个对象,也就是构造函数中的this,可以访问到挂载在this上的任意属性。
- 这个对象可以访问到构造函数原型上的属性,所以需要将对象与构造函数链接起来。
- 返回原始值需要忽略,返回对象需要正常处理。
//第一种写法
function _new(){
let obj = {}
let Con = [].shift.call(arguments)//获取构造函数
if(typeof Con !== 'function'){
return new Error('参数必须是一个函数');
}
obj.__proto__ = Con.prototype //实例与构造函数相关联
let result = Con.apply(obj,arguments)//绑定this
return result instanceof Object ? result : obj //返回
}
//第二种写法,主要用了ES6的数组展开
function _new(fn,...args){
if(typeof fn !== 'function'){
return new Error('参数必须是一个函数');
}
let obj = Object.create(fn.prototype)
let result = fn.apply(obj,args)
return result instanceof Object ? result : obj //返回
}
//构造函数
function Car(name){
this.name = name
}
Car.prototype.sayName = function(){
console.log('Name is '+ this.name)
}
let car1 = _new(Car,'BMW')
console.log(car1)//Car {name: "BMW"}
console.log(car1.sayName())//Name is BMW
实现一个instanceof
instanceof
运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。如果出现返回true,否则返回false;
function _instanceof(a,b){
while (a) {
//逐层查找
if (a.__proto__ === b.prototype) return true;
a = a.__proto__;
}
return false; //找不到返回false
}
实现一个继承
这里主要是call+原型链
组合继承
// 基类
function Base() {
}
// 派生类
function Derived() {
Base.call(this); //主要用来继承属性
}
// 将派生类的原型的原型链挂在基类的原型上
//主要用来继承方法
Object.setPrototypeOf(Derived.prototype, Base.prototype);
手写 bind、call 和 apply
它们3个都是用来改变this的指向,apply第二个参数只能传数组,bind可以修改this指向后不立即执行函数;
- bind
Function.prototype._bind = function(context = window, ...bindArgs){
//context 默认为window
const func = this //调用_bind的原函数
if (typeof func !== 'function') {
throw new TypeError('调用_bind()必须是一个函数');
}
//返回一个绑定 this 的函数
return function(...callArgs){
let args = bindArgs.concat(callArgs);
if (this instanceof func) {
// 意味着是通过 new 调用的 而 new 的优先级高于 bind
return new func(...args);
}
return func.call(context, ...args);//改变this指向
}
}
- call
Function.prototype._call = function(context = window, ...args){
context.func = this; //绑定该函数为某对象的方法
if (typeof context.func !== 'function') {
throw new TypeError('调用_call()必须是一个函数');
}
//调用方法,并返回结果
let res = context.func(...args);
delete context.func;
return res;
}
- apply
Function.prototype._apply = function(context = window, args = []) {
context.func = this;
if (typeof context.func !== 'function') {
throw new TypeError('调用_apply()必须是一个函数');
}
let res = context.func(...args);
delete context.func;
return res;
}
实现jsonp
<script>
标签的src属性并不被同源策略所约束,所以可以获取任何服务器上脚本并执行。
JSONP
的原型:创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。将JSON数据填充进回调函数。
前端代码:
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://127.0.0.1:1111?callback=onBack';
document.head.appendChild(script);
// 回调执行函数
function onBack(res) {
console.log(res)//{id: "dsji3241", name: "wangwu", age: 18}
}
后台代码:
使用了express框架创建服务
const express = require('express')
const app = express();
app.get('/',(req,res)=>{
let query = req.query
let callback = query.callback
let data = {id:'dsji3241',name:'wangwu',age:18}
res.end(callback + '(' + JSON.stringify(data) + ')')
})
app.listen(1111,()=>{
console.log('server running at 1111')
})
实现ajax
function myAjax(options){
var method = options.method || 'GET'
params = options.params,
data = options.data,
url = options.url + (params ? '?' + Object.keys(params).map(key => key + '=' + params[key]).join('&') : ''),
asy = options.asy === false ? false : true,
success = options.success,
headers = options.headers;
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
request = new ActiveXObject('Microsoft.XMLHTTP');
}
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
success && success(request.responseText);
}
}
request.open(method, url, asy);
if (headers) {
Object.keys(headers).forEach(key => request.setRequestHeader(key, headers[key]));
}
method === 'GET' ? request.send() : request.send(data);
}